diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8bde4e09dc2e..0303f1dc9ca59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -432,6 +432,6 @@ jobs: echo -e "$errors" echo " Avoid importing internal Bevy crates, they should not be used directly" echo " Fix the issue by replacing 'bevy_*' with 'bevy'" - echo " Example: 'use bevy::sprite::MaterialMesh2dBundle;' instead of 'bevy_internal::sprite::MaterialMesh2dBundle;'" + echo " Example: 'use bevy::sprite::Mesh2d;' instead of 'bevy_internal::sprite::Mesh2d;'" exit 1 fi diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 4aebd12a86a00..9bb72e2356729 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -17,7 +17,8 @@ use bevy_ecs::{ use bevy_hierarchy::{BuildChildren, ChildBuild, WorldChildBuilder}; use bevy_math::{Affine2, Mat4, Vec3}; use bevy_pbr::{ - DirectionalLight, PbrBundle, PointLight, SpotLight, StandardMaterial, UvChannel, MAX_JOINTS, + DirectionalLight, MeshMaterial3d, PointLight, SpotLight, StandardMaterial, UvChannel, + MAX_JOINTS, }; use bevy_render::{ alpha::AlphaMode, @@ -25,7 +26,7 @@ use bevy_render::{ mesh::{ morph::{MeshMorphWeights, MorphAttributes, MorphTargetImage, MorphWeights}, skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, - Indices, Mesh, MeshVertexAttribute, VertexAttributeValues, + Indices, Mesh, Mesh3d, MeshVertexAttribute, VertexAttributeValues, }, prelude::SpatialBundle, primitives::Aabb, @@ -1456,12 +1457,13 @@ fn load_node( }; let bounds = primitive.bounding_box(); - let mut mesh_entity = parent.spawn(PbrBundle { + let mut mesh_entity = parent.spawn(( // TODO: handle missing label handle errors here? - mesh: load_context.get_label_handle(primitive_label.to_string()), - material: load_context.get_label_handle(&material_label), - ..Default::default() - }); + Mesh3d(load_context.get_label_handle(primitive_label.to_string())), + MeshMaterial3d::( + load_context.get_label_handle(&material_label), + ), + )); let target_count = primitive.morph_targets().len(); if target_count != 0 { diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index 71f732752546f..55cdb3609a892 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -1,10 +1,9 @@ #![expect(deprecated)] use crate::{ - CascadeShadowConfig, Cascades, DirectionalLight, Material, PointLight, SpotLight, - StandardMaterial, + CascadeShadowConfig, Cascades, DirectionalLight, Material, MeshMaterial3d, PointLight, + SpotLight, StandardMaterial, }; -use bevy_asset::Handle; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ bundle::Bundle, @@ -14,21 +13,29 @@ use bevy_ecs::{ }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ - mesh::Mesh, + mesh::Mesh3d, primitives::{CascadesFrusta, CubemapFrusta, Frustum}, view::{InheritedVisibility, ViewVisibility, Visibility}, world_sync::SyncToRenderWorld, }; use bevy_transform::components::{GlobalTransform, Transform}; -/// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`]. +/// A component bundle for PBR entities with a [`Mesh3d`] and a [`MeshMaterial3d`]. +#[deprecated( + since = "0.15.0", + note = "Use the `Mesh3d` and `MeshMaterial3d` components instead. Inserting them will now also insert the other components required by them automatically." +)] pub type PbrBundle = MaterialMeshBundle; -/// A component bundle for entities with a [`Mesh`] and a [`Material`]. +/// A component bundle for entities with a [`Mesh3d`] and a [`MeshMaterial3d`]. #[derive(Bundle, Clone)] +#[deprecated( + since = "0.15.0", + note = "Use the `Mesh3d` and `MeshMaterial3d` components instead. Inserting them will now also insert the other components required by them automatically." +)] pub struct MaterialMeshBundle { - pub mesh: Handle, - pub material: Handle, + pub mesh: Mesh3d, + pub material: MeshMaterial3d, pub transform: Transform, pub global_transform: GlobalTransform, /// User indication of whether an entity is visible diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 1b446b57a645b..68f75af7bef86 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -34,6 +34,7 @@ mod light; mod light_probe; mod lightmap; mod material; +mod mesh_material; mod parallax; mod pbr_material; mod prepass; @@ -53,6 +54,7 @@ pub use light::*; pub use light_probe::*; pub use lightmap::*; pub use material::*; +pub use mesh_material::*; pub use parallax::*; pub use pbr_material::*; pub use prepass::*; @@ -83,6 +85,7 @@ pub mod prelude { LightProbe, }, material::{Material, MaterialPlugin}, + mesh_material::MeshMaterial3d, parallax::ParallaxMappingMethod, pbr_material::StandardMaterial, ssao::ScreenSpaceAmbientOcclusionPlugin, @@ -411,13 +414,13 @@ impl Plugin for PbrPlugin { app.add_plugins(DeferredPbrLightingPlugin); } + // Initialize the default material. app.world_mut() .resource_mut::>() .insert( &Handle::::default(), StandardMaterial { - base_color: Color::srgb(1.0, 0.0, 0.5), - unlit: true, + base_color: Color::WHITE, ..Default::default() }, ); @@ -428,7 +431,14 @@ impl Plugin for PbrPlugin { // Extract the required data from the main world render_app - .add_systems(ExtractSchedule, (extract_clusters, extract_lights)) + .add_systems( + ExtractSchedule, + ( + extract_clusters, + extract_lights, + extract_default_materials.after(clear_material_instances::), + ), + ) .add_systems( Render, ( diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index e7477a5a23639..bdcd3c567e942 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -7,7 +7,7 @@ use bevy_render::{ camera::{Camera, CameraProjection}, extract_component::ExtractComponent, extract_resource::ExtractResource, - mesh::Mesh, + mesh::Mesh3d, primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Sphere}, view::{ InheritedVisibility, NoFrustumCulling, RenderLayers, ViewVisibility, VisibilityRange, @@ -440,11 +440,11 @@ fn calculate_cascade( texel_size: cascade_texel_size, } } -/// Add this component to make a [`Mesh`] not cast shadows. +/// Add this component to make a [`Mesh3d`] not cast shadows. #[derive(Debug, Component, Reflect, Default)] #[reflect(Component, Default, Debug)] pub struct NotShadowCaster; -/// Add this component to make a [`Mesh`] not receive shadows. +/// Add this component to make a [`Mesh3d`] not receive shadows. /// /// **Note:** If you're using diffuse transmission, setting [`NotShadowReceiver`] will /// cause both “regular” shadows as well as diffusely transmitted shadows to be disabled, @@ -452,7 +452,7 @@ pub struct NotShadowCaster; #[derive(Debug, Component, Reflect, Default)] #[reflect(Component, Default, Debug)] pub struct NotShadowReceiver; -/// Add this component to make a [`Mesh`] using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0` +/// Add this component to make a [`Mesh3d`] using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0` /// receive shadows on its diffuse transmission lobe. (i.e. its “backside”) /// /// Not enabled by default, as it requires carefully setting up [`thickness`](crate::pbr_material::StandardMaterial::thickness) @@ -697,7 +697,7 @@ pub fn check_dir_light_mesh_visibility( ( Without, Without, - With>, + With, ), >, visible_entity_ranges: Option>, @@ -866,7 +866,7 @@ pub fn check_point_light_mesh_visibility( ( Without, Without, - With>, + With, ), >, visible_entity_ranges: Option>, diff --git a/crates/bevy_pbr/src/lightmap/mod.rs b/crates/bevy_pbr/src/lightmap/mod.rs index d1fd1cc143b8c..f1b03f97af3f8 100644 --- a/crates/bevy_pbr/src/lightmap/mod.rs +++ b/crates/bevy_pbr/src/lightmap/mod.rs @@ -6,10 +6,10 @@ //! with an addon like [The Lightmapper]. The tools in the [`bevy-baked-gi`] //! project support other lightmap baking methods. //! -//! When a [`Lightmap`] component is added to an entity with a [`Mesh`] and a -//! [`StandardMaterial`](crate::StandardMaterial), Bevy applies the lightmap when rendering. The brightness +//! When a [`Lightmap`] component is added to an entity with a [`Mesh3d`] and a +//! [`MeshMaterial3d`], Bevy applies the lightmap when rendering. The brightness //! of the lightmap may be controlled with the `lightmap_exposure` field on -//! `StandardMaterial`. +//! [`StandardMaterial`]. //! //! During the rendering extraction phase, we extract all lightmaps into the //! [`RenderLightmaps`] table, which lives in the render world. Mesh bindgroup @@ -25,7 +25,9 @@ //! appropriately. //! //! [The Lightmapper]: https://github.com/Naxela/The_Lightmapper -//! +//! [`Mesh3d`]: bevy_render::mesh::Mesh3d +//! [`MeshMaterial3d`]: crate::StandardMaterial +//! [`StandardMaterial`]: crate::StandardMaterial //! [`bevy-baked-gi`]: https://github.com/pcwalton/bevy-baked-gi use bevy_app::{App, Plugin}; @@ -61,10 +63,10 @@ pub struct LightmapPlugin; /// A component that applies baked indirect diffuse global illumination from a /// lightmap. /// -/// When assigned to an entity that contains a [`Mesh`] and a -/// [`StandardMaterial`](crate::StandardMaterial), if the mesh has a second UV -/// layer ([`ATTRIBUTE_UV_1`](bevy_render::mesh::Mesh::ATTRIBUTE_UV_1)), then -/// the lightmap will render using those UVs. +/// When assigned to an entity that contains a [`Mesh3d`](bevy_render::mesh::Mesh3d) and a +/// [`MeshMaterial3d`](crate::StandardMaterial), if the mesh +/// has a second UV layer ([`ATTRIBUTE_UV_1`](bevy_render::mesh::Mesh::ATTRIBUTE_UV_1)), +/// then the lightmap will render using those UVs. #[derive(Component, Clone, Reflect)] #[reflect(Component, Default)] pub struct Lightmap { diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index c08555744bb56..29a3a85b342dd 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -24,14 +24,15 @@ use bevy_reflect::std_traits::ReflectDefault; use bevy_reflect::Reflect; use bevy_render::{ camera::TemporalJitter, - extract_instances::{ExtractInstancesPlugin, ExtractedInstances}, + extract_instances::ExtractedInstances, extract_resource::ExtractResource, - mesh::{MeshVertexBufferLayoutRef, RenderMesh}, + mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh}, render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets}, render_phase::*, render_resource::*, renderer::RenderDevice, - view::{ExtractedView, Msaa, RenderVisibilityRanges, VisibleEntities, WithMesh}, + view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility, VisibleEntities}, + Extract, }; use bevy_utils::tracing::error; use core::{ @@ -43,26 +44,28 @@ use core::{ use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight}; -/// Materials are used alongside [`MaterialPlugin`] and [`MaterialMeshBundle`] +/// Materials are used alongside [`MaterialPlugin`], [`Mesh3d`], and [`MeshMaterial3d`] /// to spawn entities that are rendered with a specific [`Material`] type. They serve as an easy to use high level -/// way to render [`Mesh`](bevy_render::mesh::Mesh) entities with custom shader logic. +/// way to render [`Mesh3d`] entities with custom shader logic. /// /// Materials must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders. /// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details. /// /// # Example /// -/// Here is a simple Material implementation. The [`AsBindGroup`] derive has many features. To see what else is available, +/// Here is a simple [`Material`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available, /// check out the [`AsBindGroup`] documentation. +/// /// ``` -/// # use bevy_pbr::{Material, MaterialMeshBundle}; +/// # use bevy_pbr::{Material, MeshMaterial3d}; /// # use bevy_ecs::prelude::*; /// # use bevy_reflect::TypePath; -/// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image}; +/// # use bevy_render::{mesh::{Mesh, Mesh3d}, render_resource::{AsBindGroup, ShaderRef}, texture::Image}; /// # use bevy_color::LinearRgba; /// # use bevy_color::palettes::basic::RED; /// # use bevy_asset::{Handle, AssetServer, Assets, Asset}; -/// +/// # use bevy_math::primitives::Capsule3d; +/// # /// #[derive(AsBindGroup, Debug, Clone, Asset, TypePath)] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -84,17 +87,23 @@ use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight}; /// } /// } /// -/// // Spawn an entity using `CustomMaterial`. -/// fn setup(mut commands: Commands, mut materials: ResMut>, asset_server: Res) { -/// commands.spawn(MaterialMeshBundle { -/// material: materials.add(CustomMaterial { +/// // Spawn an entity with a mesh using `CustomMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// asset_server: Res +/// ) { +/// commands.spawn(( +/// Mesh3d(meshes.add(Capsule3d::default())), +/// MeshMaterial3d(materials.add(CustomMaterial { /// color: RED.into(), /// color_texture: asset_server.load("some_image.png"), -/// }), -/// ..Default::default() -/// }); +/// })), +/// )); /// } /// ``` +/// /// In WGSL shaders, the material's binding would look like this: /// /// ```wgsl @@ -258,20 +267,25 @@ where M::Data: PartialEq + Eq + Hash + Clone, { fn build(&self, app: &mut App) { - app.init_asset::().add_plugins(( - ExtractInstancesPlugin::>::extract_visible(), - RenderAssetPlugin::>::default(), - )); + app.init_asset::() + .register_type::>() + .register_type::() + .add_plugins(RenderAssetPlugin::>::default()); if let Some(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::>() + .init_resource::>>() .add_render_command::>() .add_render_command::>() .add_render_command::>() .add_render_command::>() .add_render_command::>() .init_resource::>>() + .add_systems( + ExtractSchedule, + (clear_material_instances::, extract_mesh_materials::).chain(), + ) .add_systems( Render, queue_material_meshes:: @@ -527,6 +541,35 @@ pub const fn screen_space_specular_transmission_pipeline_key( } } +pub(super) fn clear_material_instances( + mut material_instances: ResMut>, +) { + material_instances.clear(); +} + +fn extract_mesh_materials( + mut material_instances: ResMut>, + query: Extract), With>>, +) { + for (entity, view_visibility, material) in &query { + if view_visibility.get() { + material_instances.insert(entity, material.id()); + } + } +} + +/// Extracts default materials for 3D meshes with no [`MeshMaterial3d`]. +pub(super) fn extract_default_materials( + mut material_instances: ResMut>, + query: Extract, Without)>>, +) { + for (entity, view_visibility) in &query { + if view_visibility.get() { + material_instances.insert(entity, AssetId::default()); + } + } +} + /// For each view, iterates over all the meshes visible from that view and adds /// them to [`BinnedRenderPhase`]s or [`SortedRenderPhase`]s as appropriate. #[allow(clippy::too_many_arguments)] @@ -686,7 +729,7 @@ pub fn queue_material_meshes( } let rangefinder = view.rangefinder3d(); - for visible_entity in visible_entities.iter::() { + for visible_entity in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/mesh_material.rs b/crates/bevy_pbr/src/mesh_material.rs new file mode 100644 index 0000000000000..90ee6ea9dc8ad --- /dev/null +++ b/crates/bevy_pbr/src/mesh_material.rs @@ -0,0 +1,106 @@ +use crate::Material; +use bevy_asset::{AssetId, Handle}; +use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; +use bevy_reflect::{std_traits::ReflectDefault, Reflect}; + +/// A [material](Material) for a [`Mesh3d`]. +/// +/// See [`Material`] for general information about 3D materials and how to implement your own materials. +/// +/// [`Mesh3d`]: bevy_render::mesh::Mesh3d +/// +/// # Example +/// +/// ``` +/// # use bevy_pbr::{Material, MeshMaterial3d, StandardMaterial}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::{Mesh, Mesh3d}; +/// # use bevy_color::palettes::basic::RED; +/// # use bevy_asset::Assets; +/// # use bevy_math::primitives::Capsule3d; +/// # +/// // Spawn an entity with a mesh using `StandardMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// commands.spawn(( +/// Mesh3d(meshes.add(Capsule3d::default())), +/// MeshMaterial3d(materials.add(StandardMaterial { +/// base_color: RED.into(), +/// ..Default::default() +/// })), +/// )); +/// } +/// ``` +/// +/// ## Default Material +/// +/// Meshes without a [`MeshMaterial3d`] are rendered with a default [`StandardMaterial`]. +/// This material can be overridden by inserting a custom material for the default asset handle. +/// +/// ``` +/// # use bevy_pbr::{Material, MeshMaterial3d, StandardMaterial}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::{Mesh, Mesh3d}; +/// # use bevy_color::Color; +/// # use bevy_asset::{Assets, Handle}; +/// # use bevy_math::primitives::Capsule3d; +/// # +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// // Optional: Insert a custom default material. +/// materials.insert( +/// &Handle::::default(), +/// StandardMaterial::from(Color::srgb(1.0, 0.0, 1.0)), +/// ); +/// +/// // Spawn a capsule with no material. +/// // The mesh will be rendered with the default material. +/// commands.spawn(Mesh3d(meshes.add(Capsule3d::default()))); +/// } +/// ``` +/// +/// [`StandardMaterial`]: crate::StandardMaterial +#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq)] +#[reflect(Component, Default)] +#[require(HasMaterial3d)] +pub struct MeshMaterial3d(pub Handle); + +impl Default for MeshMaterial3d { + fn default() -> Self { + Self(Handle::default()) + } +} + +impl From> for MeshMaterial3d { + fn from(handle: Handle) -> Self { + Self(handle) + } +} + +impl From> for AssetId { + fn from(material: MeshMaterial3d) -> Self { + material.id() + } +} + +impl From<&MeshMaterial3d> for AssetId { + fn from(material: &MeshMaterial3d) -> Self { + material.id() + } +} + +/// A component that marks an entity as having a [`MeshMaterial3d`]. +/// [`Mesh3d`] entities without this component are rendered with a [default material]. +/// +/// [`Mesh3d`]: bevy_render::mesh::Mesh3d +/// [default material]: crate::MeshMaterial3d#default-material +#[derive(Component, Clone, Debug, Default, Reflect)] +#[reflect(Component, Default)] +pub struct HasMaterial3d; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 166ef0c2cc1c1..ee04e6da91c84 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -1,9 +1,8 @@ mod prepass_bindings; use bevy_render::{ - mesh::{MeshVertexBufferLayoutRef, RenderMesh}, + mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh}, render_resource::binding_types::uniform_buffer, - view::WithMesh, world_sync::RenderEntity, }; pub use prepass_bindings::*; @@ -220,9 +219,9 @@ pub fn update_previous_view_data( pub struct PreviousGlobalTransform(pub Affine3A); #[cfg(not(feature = "meshlet"))] -type PreviousMeshFilter = With>; +type PreviousMeshFilter = With; #[cfg(feature = "meshlet")] -type PreviousMeshFilter = Or<(With>, With>)>; +type PreviousMeshFilter = Or<(With, With>)>; pub fn update_mesh_previous_global_transforms( mut commands: Commands, @@ -766,7 +765,7 @@ pub fn queue_prepass_material_meshes( view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } - for visible_entity in visible_entities.iter::() { + for visible_entity in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index c1c9a29129563..91f93c61b2560 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -567,7 +567,7 @@ pub struct RenderMeshInstanceGpuQueues(Parallel); impl RenderMeshInstanceShared { fn from_components( previous_transform: Option<&PreviousGlobalTransform>, - handle: &Handle, + mesh: &Mesh3d, not_shadow_caster: bool, no_automatic_batching: bool, ) -> Self { @@ -583,8 +583,7 @@ impl RenderMeshInstanceShared { ); RenderMeshInstanceShared { - mesh_asset_id: handle.id(), - + mesh_asset_id: mesh.id(), flags: mesh_instance_flags, material_bind_group_id: AtomicMaterialBindGroupId::default(), } @@ -870,7 +869,7 @@ pub fn extract_meshes_for_cpu_building( &ViewVisibility, &GlobalTransform, Option<&PreviousGlobalTransform>, - &Handle, + &Mesh3d, Has, Has, Has, @@ -887,7 +886,7 @@ pub fn extract_meshes_for_cpu_building( view_visibility, transform, previous_transform, - handle, + mesh, not_shadow_receiver, transmitted_receiver, not_shadow_caster, @@ -912,7 +911,7 @@ pub fn extract_meshes_for_cpu_building( let shared = RenderMeshInstanceShared::from_components( previous_transform, - handle, + mesh, not_shadow_caster, no_automatic_batching, ); @@ -969,7 +968,7 @@ pub fn extract_meshes_for_gpu_building( Option<&PreviousGlobalTransform>, Option<&Lightmap>, Option<&Aabb>, - &Handle, + &Mesh3d, Has, Has, Has, @@ -1003,7 +1002,7 @@ pub fn extract_meshes_for_gpu_building( previous_transform, lightmap, aabb, - handle, + mesh, not_shadow_receiver, transmitted_receiver, not_shadow_caster, @@ -1028,7 +1027,7 @@ pub fn extract_meshes_for_gpu_building( let shared = RenderMeshInstanceShared::from_components( previous_transform, - handle, + mesh, not_shadow_caster, no_automatic_batching, ); diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 86e8bd49e914e..2a1ee1a278acd 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -1,11 +1,13 @@ -use crate::{Material, MaterialPipeline, MaterialPipelineKey, MaterialPlugin}; +use crate::{Material, MaterialPipeline, MaterialPipelineKey, MaterialPlugin, MeshMaterial3d}; use bevy_app::{Plugin, Startup, Update}; use bevy_asset::{load_internal_asset, Asset, Assets, Handle}; use bevy_color::{Color, LinearRgba}; use bevy_ecs::prelude::*; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; use bevy_render::{ - extract_resource::ExtractResource, mesh::MeshVertexBufferLayoutRef, prelude::*, + extract_resource::ExtractResource, + mesh::{Mesh3d, MeshVertexBufferLayoutRef}, + prelude::*, render_resource::*, }; @@ -129,12 +131,12 @@ fn global_color_changed( fn wireframe_color_changed( mut materials: ResMut>, mut colors_changed: Query< - (&mut Handle, &WireframeColor), + (&mut MeshMaterial3d, &WireframeColor), (With, Changed), >, ) { for (mut handle, wireframe_color) in &mut colors_changed { - *handle = materials.add(WireframeMaterial { + handle.0 = materials.add(WireframeMaterial { color: wireframe_color.color.into(), }); } @@ -147,27 +149,27 @@ fn apply_wireframe_material( mut materials: ResMut>, wireframes: Query< (Entity, Option<&WireframeColor>), - (With, Without>), + (With, Without>), >, - no_wireframes: Query, With>)>, + no_wireframes: Query, With>)>, mut removed_wireframes: RemovedComponents, global_material: Res, ) { for e in removed_wireframes.read().chain(no_wireframes.iter()) { if let Some(commands) = commands.get_entity(e) { - commands.remove::>(); + commands.remove::>(); } } let mut material_to_spawn = vec![]; for (e, maybe_color) in &wireframes { let material = get_wireframe_material(maybe_color, &mut materials, &global_material); - material_to_spawn.push((e, material)); + material_to_spawn.push((e, MeshMaterial3d(material))); } commands.insert_or_spawn_batch(material_to_spawn); } -type WireframeFilter = (With>, Without, Without); +type WireframeFilter = (With, Without, Without); /// Applies or removes a wireframe material on any mesh without a [`Wireframe`] or [`NoWireframe`] component. fn apply_global_wireframe_material( @@ -175,9 +177,12 @@ fn apply_global_wireframe_material( config: Res, meshes_without_material: Query< (Entity, Option<&WireframeColor>), - (WireframeFilter, Without>), + (WireframeFilter, Without>), + >, + meshes_with_global_material: Query< + Entity, + (WireframeFilter, With>), >, - meshes_with_global_material: Query>)>, global_material: Res, mut materials: ResMut>, ) { @@ -187,12 +192,14 @@ fn apply_global_wireframe_material( let material = get_wireframe_material(maybe_color, &mut materials, &global_material); // We only add the material handle but not the Wireframe component // This makes it easy to detect which mesh is using the global material and which ones are user specified - material_to_spawn.push((e, material)); + material_to_spawn.push((e, MeshMaterial3d(material))); } commands.insert_or_spawn_batch(material_to_spawn); } else { for e in &meshes_with_global_material { - commands.entity(e).remove::>(); + commands + .entity(e) + .remove::>(); } } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 51bd01c612d30..40a8c1ebb0d5e 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -54,7 +54,10 @@ pub mod prelude { Camera, ClearColor, ClearColorConfig, OrthographicProjection, PerspectiveProjection, Projection, }, - mesh::{morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh}, + mesh::{ + morph::MorphWeights, primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d, + Mesh3d, + }, render_resource::Shader, spatial_bundle::SpatialBundle, texture::{image_texture_conversion::IntoDynamicImageError, Image, ImagePlugin}, diff --git a/crates/bevy_render/src/mesh/components.rs b/crates/bevy_render/src/mesh/components.rs new file mode 100644 index 0000000000000..89ff93e76bfa8 --- /dev/null +++ b/crates/bevy_render/src/mesh/components.rs @@ -0,0 +1,115 @@ +use crate::{mesh::Mesh, view::Visibility}; +use bevy_asset::{AssetId, Handle}; +use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::{component::Component, reflect::ReflectComponent}; +use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_transform::components::Transform; + +/// A component for rendering 2D meshes, typically with a [`MeshMaterial2d`] using a [`ColorMaterial`]. +/// +/// Meshes without a [`MeshMaterial2d`] will be rendered with a [default material]. +/// +/// [`MeshMaterial2d`]: +/// [`ColorMaterial`]: +/// [default material]: +/// +/// # Example +/// +/// ```ignore +/// # use bevy_sprite::{ColorMaterial, Mesh2d, MeshMaterial2d}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::Mesh; +/// # use bevy_color::palettes::basic::RED; +/// # use bevy_asset::Assets; +/// # use bevy_math::primitives::Circle; +/// # +/// // Spawn an entity with a mesh using `ColorMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// commands.spawn(( +/// Mesh2d(meshes.add(Circle::new(50.0))), +/// MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))), +/// )); +/// } +/// ``` +#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq)] +#[reflect(Component, Default)] +#[require(Transform, Visibility)] +pub struct Mesh2d(pub Handle); + +impl From> for Mesh2d { + fn from(handle: Handle) -> Self { + Self(handle) + } +} + +impl From for AssetId { + fn from(mesh: Mesh2d) -> Self { + mesh.id() + } +} + +impl From<&Mesh2d> for AssetId { + fn from(mesh: &Mesh2d) -> Self { + mesh.id() + } +} + +/// A component for rendering 3D meshes, typically with a [`MeshMaterial3d`] using a [`StandardMaterial`]. +/// +/// Meshes without a [`MeshMaterial3d`] will be rendered with a [default material]. +/// +/// [`MeshMaterial3d`]: +/// [`StandardMaterial`]: +/// [default material]: +/// +/// # Example +/// +/// ```ignore +/// # use bevy_pbr::{Material, MeshMaterial3d, StandardMaterial}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::{Mesh, Mesh3d}; +/// # use bevy_color::palettes::basic::RED; +/// # use bevy_asset::Assets; +/// # use bevy_math::primitives::Capsule3d; +/// # +/// // Spawn an entity with a mesh using `StandardMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// commands.spawn(( +/// Mesh3d(meshes.add(Capsule3d::default())), +/// MeshMaterial3d(materials.add(StandardMaterial { +/// base_color: RED.into(), +/// ..Default::default() +/// })), +/// )); +/// } +/// ``` +#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq)] +#[reflect(Component, Default)] +#[require(Transform, Visibility)] +pub struct Mesh3d(pub Handle); + +impl From> for Mesh3d { + fn from(handle: Handle) -> Self { + Self(handle) + } +} + +impl From for AssetId { + fn from(mesh: Mesh3d) -> Self { + mesh.id() + } +} + +impl From<&Mesh3d> for AssetId { + fn from(mesh: &Mesh3d) -> Self { + mesh.id() + } +} diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index 20271a122e187..1f40cb8b41a4c 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -36,11 +36,10 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; /// /// Meshes can be automatically generated by a bevy `AssetLoader` (generally by loading a `Gltf` file), /// or by converting a [primitive](bevy_math::primitives) using [`into`](Into). -/// It is also possible to create one manually. -/// They can be edited after creation. +/// It is also possible to create one manually. They can be edited after creation. /// -/// Meshes can be rendered with a `Material`, like `StandardMaterial` in `PbrBundle` -/// or `ColorMaterial` in `ColorMesh2dBundle`. +/// Meshes can be rendered with a [`Mesh2d`](super::Mesh2d) and `MeshMaterial2d` +/// or [`Mesh3d`](super::Mesh3d) and `MeshMaterial3d` for 2D and 3D respectively. /// /// A [`Mesh`] in Bevy is equivalent to a "primitive" in the glTF format, for a /// glTF Mesh representation, see `GltfMesh`. @@ -49,6 +48,7 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; /// /// The following function will construct a flat mesh, to be rendered with a /// `StandardMaterial` or `ColorMaterial`: +/// /// ``` /// # use bevy_render::mesh::{Mesh, Indices}; /// # use bevy_render::render_resource::PrimitiveTopology; @@ -84,7 +84,7 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; /// ``` /// /// You can see how it looks like [here](https://github.com/bevyengine/bevy/blob/main/assets/docs/Mesh.png), -/// used in a `PbrBundle` with a square bevy logo texture, with added axis, points, +/// used in a `Mesh3d` with a square bevy logo texture, with added axis, points, /// lines and text for clarity. /// /// ## Other examples @@ -92,7 +92,7 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; /// For further visualization, explanation, and examples, see the built-in Bevy examples, /// and the [implementation of the built-in shapes](https://github.com/bevyengine/bevy/tree/main/crates/bevy_render/src/mesh/primitives). /// In particular, [generate_custom_mesh](https://github.com/bevyengine/bevy/blob/main/examples/3d/generate_custom_mesh.rs) -/// teaches you to access modify a Mesh's attributes after creating it. +/// teaches you to access and modify the attributes of a [`Mesh`] after creating it. /// /// ## Common points of confusion /// diff --git a/crates/bevy_render/src/mesh/mod.rs b/crates/bevy_render/src/mesh/mod.rs index 6897e1d176f74..3b6572ea100f9 100644 --- a/crates/bevy_render/src/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mod.rs @@ -2,12 +2,14 @@ mod mesh; pub mod allocator; +mod components; pub mod morph; pub mod primitives; use alloc::sync::Arc; use allocator::MeshAllocatorPlugin; use bevy_utils::HashSet; +pub use components::{Mesh2d, Mesh3d}; use core::hash::{Hash, Hasher}; pub use mesh::*; pub use primitives::*; @@ -25,6 +27,7 @@ impl Plugin for MeshPlugin { app.init_asset::() .init_asset::() .register_asset_reflect::() + .register_type::() .register_type::() .register_type::>() // 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready diff --git a/crates/bevy_render/src/mesh/morph.rs b/crates/bevy_render/src/mesh/morph.rs index db28ed5afea0e..3096842e73b11 100644 --- a/crates/bevy_render/src/mesh/morph.rs +++ b/crates/bevy_render/src/mesh/morph.rs @@ -14,6 +14,8 @@ use bytemuck::{Pod, Zeroable}; use core::iter; use thiserror::Error; +use super::Mesh3d; + const MAX_TEXTURE_WIDTH: u32 = 2048; // NOTE: "component" refers to the element count of math objects, // Vec3 has 3 components, Mat2 has 4 components. @@ -114,7 +116,7 @@ impl MorphTargetImage { } } -/// Controls the [morph targets] for all child [`Handle`] entities. In most cases, [`MorphWeights`] should be considered +/// Controls the [morph targets] for all child [`Mesh3d`] entities. In most cases, [`MorphWeights`] should be considered /// the "source of truth" when writing morph targets for meshes. However you can choose to write child [`MeshMorphWeights`] /// if your situation requires more granularity. Just note that if you set [`MorphWeights`], it will overwrite child /// [`MeshMorphWeights`] values. @@ -122,9 +124,9 @@ impl MorphTargetImage { /// This exists because Bevy's [`Mesh`] corresponds to a _single_ surface / material, whereas morph targets /// as defined in the GLTF spec exist on "multi-primitive meshes" (where each primitive is its own surface with its own material). /// Therefore in Bevy [`MorphWeights`] an a parent entity are the "canonical weights" from a GLTF perspective, which then -/// synchronized to child [`Handle`] / [`MeshMorphWeights`] (which correspond to "primitives" / "surfaces" from a GLTF perspective). +/// synchronized to child [`Mesh3d`] / [`MeshMorphWeights`] (which correspond to "primitives" / "surfaces" from a GLTF perspective). /// -/// Add this to the parent of one or more [`Entities`](`Entity`) with a [`Handle`] with a [`MeshMorphWeights`]. +/// Add this to the parent of one or more [`Entities`](`Entity`) with a [`Mesh3d`] with a [`MeshMorphWeights`]. /// /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation #[derive(Reflect, Default, Debug, Clone, Component)] @@ -148,7 +150,7 @@ impl MorphWeights { first_mesh, }) } - /// The first child [`Handle`] primitive controlled by these weights. + /// The first child [`Mesh3d`] primitive controlled by these weights. /// This can be used to look up metadata information such as [`Mesh::morph_target_names`]. pub fn first_mesh(&self) -> Option<&Handle> { self.first_mesh.as_ref() @@ -168,7 +170,7 @@ impl MorphWeights { /// /// See [`MorphWeights`] for more details on Bevy's morph target implementation. /// -/// Add this to an [`Entity`] with a [`Handle`] with a [`MorphAttributes`] set +/// Add this to an [`Entity`] with a [`Mesh3d`] with a [`MorphAttributes`] set /// to control individual weights of each morph target. /// /// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation @@ -198,8 +200,8 @@ impl MeshMorphWeights { /// /// Only direct children are updated, to fulfill the expectations of glTF spec. pub fn inherit_weights( - morph_nodes: Query<(&Children, &MorphWeights), (Without>, Changed)>, - mut morph_primitives: Query<&mut MeshMorphWeights, With>>, + morph_nodes: Query<(&Children, &MorphWeights), (Without, Changed)>, + mut morph_primitives: Query<&mut MeshMorphWeights, With>, ) { for (children, parent_weights) in &morph_nodes { let mut iter = morph_primitives.iter_many_mut(children); diff --git a/crates/bevy_render/src/primitives/mod.rs b/crates/bevy_render/src/primitives/mod.rs index c2909b795b295..66925005ef956 100644 --- a/crates/bevy_render/src/primitives/mod.rs +++ b/crates/bevy_render/src/primitives/mod.rs @@ -17,19 +17,17 @@ use bevy_reflect::prelude::*; /// with the camera's [`Frustum`]. /// /// It will be added automatically by the systems in [`CalculateBounds`] to entities that: -/// - could be subject to frustum culling, for example with a [`Handle`] +/// - could be subject to frustum culling, for example with a [`Mesh3d`] /// or `Sprite` component, /// - don't have the [`NoFrustumCulling`] component. /// /// It won't be updated automatically if the space occupied by the entity changes, -/// for example if the vertex positions of a [`Mesh`] inside a `Handle` are -/// updated. +/// for example if the vertex positions of a [`Mesh3d`] are updated. /// /// [`Camera`]: crate::camera::Camera /// [`NoFrustumCulling`]: crate::view::visibility::NoFrustumCulling /// [`CalculateBounds`]: crate::view::visibility::VisibilitySystems::CalculateBounds -/// [`Mesh`]: crate::mesh::Mesh -/// [`Handle`]: crate::mesh::Mesh +/// [`Mesh3d`]: crate::mesh::Mesh #[derive(Component, Clone, Copy, Debug, Default, Reflect, PartialEq)] #[reflect(Component, Default, Debug, PartialEq)] pub struct Aabb { diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 68e784dd09a7c..8f9a8748d69d0 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -9,7 +9,7 @@ pub use range::*; pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; -use bevy_asset::{Assets, Handle}; +use bevy_asset::Assets; use bevy_derive::Deref; use bevy_ecs::{prelude::*, query::QueryFilter}; use bevy_hierarchy::{Children, Parent}; @@ -19,7 +19,7 @@ use bevy_utils::{Parallel, TypeIdMap}; use crate::{ camera::{Camera, CameraProjection}, - mesh::Mesh, + mesh::{Mesh, Mesh3d}, primitives::{Aabb, Frustum, Sphere}, }; @@ -271,10 +271,6 @@ impl VisibleEntities { } } -/// A convenient alias for `With>`, for use with -/// [`VisibleEntities`]. -pub type WithMesh = With>; - #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum VisibilitySystems { /// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems, @@ -312,20 +308,20 @@ impl Plugin for VisibilityPlugin { ( calculate_bounds.in_set(CalculateBounds), (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate), - check_visibility::.in_set(CheckVisibility), + check_visibility::>.in_set(CheckVisibility), ), ); } } /// Computes and adds an [`Aabb`] component to entities with a -/// [`Handle`](Mesh) component and without a [`NoFrustumCulling`] component. +/// [`Mesh3d`] component and without a [`NoFrustumCulling`] component. /// /// This system is used in system set [`VisibilitySystems::CalculateBounds`]. pub fn calculate_bounds( mut commands: Commands, meshes: Res>, - without_aabb: Query<(Entity, &Handle), (Without, Without)>, + without_aabb: Query<(Entity, &Mesh3d), (Without, Without)>, ) { for (entity, mesh_handle) in &without_aabb { if let Some(mesh) = meshes.get(mesh_handle) { diff --git a/crates/bevy_render/src/view/visibility/range.rs b/crates/bevy_render/src/view/visibility/range.rs index cbf93d2b2674a..74e089ed9ffc1 100644 --- a/crates/bevy_render/src/view/visibility/range.rs +++ b/crates/bevy_render/src/view/visibility/range.rs @@ -24,13 +24,14 @@ use wgpu::{BufferBindingType, BufferUsages}; use crate::{ camera::Camera, + mesh::Mesh3d, primitives::Aabb, render_resource::BufferVec, renderer::{RenderDevice, RenderQueue}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use super::{check_visibility, VisibilitySystems, WithMesh}; +use super::{check_visibility, VisibilitySystems}; /// We need at least 4 storage buffer bindings available to enable the /// visibility range buffer. @@ -57,7 +58,7 @@ impl Plugin for VisibilityRangePlugin { PostUpdate, check_visibility_ranges .in_set(VisibilitySystems::CheckVisibility) - .before(check_visibility::), + .before(check_visibility::>), ); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index a5d645e001873..fd6562997f546 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -25,6 +25,7 @@ mod texture_slice; /// The sprite prelude. /// /// This includes the most common types in this crate, re-exported for your convenience. +#[expect(deprecated)] pub mod prelude { #[doc(hidden)] pub use crate::{ @@ -32,7 +33,7 @@ pub mod prelude { sprite::{ImageScaleMode, Sprite}, texture_atlas::{TextureAtlas, TextureAtlasLayout, TextureAtlasSources}, texture_slice::{BorderRect, SliceScaleMode, TextureSlice, TextureSlicer}, - ColorMaterial, ColorMesh2dBundle, TextureAtlasBuilder, + ColorMaterial, ColorMesh2dBundle, MeshMaterial2d, TextureAtlasBuilder, }; } @@ -52,7 +53,7 @@ use bevy_core_pipeline::core_2d::Transparent2d; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ extract_component::{ExtractComponent, ExtractComponentPlugin}, - mesh::Mesh, + mesh::{Mesh, Mesh2d}, primitives::Aabb, render_phase::AddRenderCommand, render_resource::{Shader, SpecializedRenderPipelines}, @@ -84,10 +85,6 @@ pub enum SpriteSystem { #[reflect(Component, Default, Debug)] pub struct SpriteSource; -/// A convenient alias for `With>`, for use with -/// [`bevy_render::view::VisibleEntities`]. -pub type WithMesh2d = With; - /// A convenient alias for `Or, With>`, for use with /// [`bevy_render::view::VisibleEntities`]. pub type WithSprite = Or<(With, With)>; @@ -113,7 +110,7 @@ impl Plugin for SpritePlugin { .register_type::() .register_type::() .register_type::() - .register_type::() + .register_type::() .register_type::() .add_plugins(( Mesh2dRenderPlugin, @@ -130,7 +127,7 @@ impl Plugin for SpritePlugin { ) .in_set(SpriteSystem::ComputeSlices), ( - check_visibility::, + check_visibility::>, check_visibility::, ) .in_set(VisibilitySystems::CheckVisibility), @@ -176,7 +173,7 @@ impl Plugin for SpritePlugin { } /// System calculating and inserting an [`Aabb`] component to entities with either: -/// - a `Mesh2dHandle` component, +/// - a `Mesh2d` component, /// - a `Sprite` and `Handle` components, /// and without a [`NoFrustumCulling`] component. /// @@ -186,7 +183,7 @@ pub fn calculate_bounds_2d( meshes: Res>, images: Res>, atlases: Res>, - meshes_without_aabb: Query<(Entity, &Mesh2dHandle), (Without, Without)>, + meshes_without_aabb: Query<(Entity, &Mesh2d), (Without, Without)>, sprites_to_recalculate_aabb: Query< (Entity, &Sprite, &Handle, Option<&TextureAtlas>), ( diff --git a/crates/bevy_sprite/src/mesh2d/color_material.rs b/crates/bevy_sprite/src/mesh2d/color_material.rs index 2df333539a2b5..f2e5aab14e5ea 100644 --- a/crates/bevy_sprite/src/mesh2d/color_material.rs +++ b/crates/bevy_sprite/src/mesh2d/color_material.rs @@ -1,13 +1,20 @@ -use crate::{AlphaMode2d, Material2d, Material2dPlugin, MaterialMesh2dBundle}; +#![expect(deprecated)] + +use crate::{ + clear_material_2d_instances, extract_default_materials_2d, AlphaMode2d, Material2d, + Material2dPlugin, MaterialMesh2dBundle, +}; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Asset, AssetApp, Assets, Handle}; use bevy_color::{Alpha, Color, ColorToComponents, LinearRgba}; +use bevy_ecs::schedule::IntoSystemConfigs; use bevy_math::Vec4; use bevy_reflect::prelude::*; use bevy_render::{ render_asset::RenderAssets, render_resource::*, texture::{GpuImage, Image}, + ExtractSchedule, RenderApp, }; pub const COLOR_MATERIAL_SHADER_HANDLE: Handle = @@ -28,19 +35,30 @@ impl Plugin for ColorMaterialPlugin { app.add_plugins(Material2dPlugin::::default()) .register_asset_reflect::(); + // Initialize the default material. app.world_mut() .resource_mut::>() .insert( &Handle::::default(), ColorMaterial { - color: Color::srgb(1.0, 0.0, 1.0), + color: Color::WHITE, ..Default::default() }, ); + + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { + return; + }; + + // Extract default materials for entities with no material. + render_app.add_systems( + ExtractSchedule, + extract_default_materials_2d.after(clear_material_2d_instances::), + ); } } -/// A [2d material](Material2d) that renders [2d meshes](crate::Mesh2dHandle) with a texture tinted by a uniform color +/// A [2d material](Material2d) that renders [2d meshes](crate::Mesh2d) with a texture tinted by a uniform color #[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] #[reflect(Default, Debug)] #[uniform(0, ColorMaterialUniform)] @@ -158,5 +176,9 @@ impl Material2d for ColorMaterial { } } -/// A component bundle for entities with a [`Mesh2dHandle`](crate::Mesh2dHandle) and a [`ColorMaterial`]. +/// A component bundle for entities with a [`Mesh2d`](crate::Mesh2d) and a [`ColorMaterial`]. +#[deprecated( + since = "0.15.0", + note = "Use the `Mesh3d` and `MeshMaterial3d` components instead. Inserting them will now also insert the other components required by them automatically." +)] pub type ColorMesh2dBundle = MaterialMesh2dBundle; diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index be3010792a0f4..d773c386ebddc 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -1,3 +1,5 @@ +#![expect(deprecated)] + use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetApp, AssetId, AssetServer, Handle}; use bevy_core_pipeline::{ @@ -36,29 +38,34 @@ use bevy_utils::tracing::error; use core::{hash::Hash, marker::PhantomData}; use crate::{ - DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances, - SetMesh2dBindGroup, SetMesh2dViewBindGroup, WithMesh2d, + DrawMesh2d, Mesh2d, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances, + SetMesh2dBindGroup, SetMesh2dViewBindGroup, }; -/// Materials are used alongside [`Material2dPlugin`] and [`MaterialMesh2dBundle`] +use super::ColorMaterial; + +/// Materials are used alongside [`Material2dPlugin`], [`Mesh2d`], and [`MeshMaterial2d`] /// to spawn entities that are rendered with a specific [`Material2d`] type. They serve as an easy to use high level -/// way to render [`Mesh2dHandle`] entities with custom shader logic. +/// way to render [`Mesh2d`] entities with custom shader logic. /// -/// Material2ds must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders. +/// Materials must implement [`AsBindGroup`] to define how data will be transferred to the GPU and bound in shaders. /// [`AsBindGroup`] can be derived, which makes generating bindings straightforward. See the [`AsBindGroup`] docs for details. /// /// # Example /// -/// Here is a simple Material2d implementation. The [`AsBindGroup`] derive has many features. To see what else is available, +/// Here is a simple [`Material2d`] implementation. The [`AsBindGroup`] derive has many features. To see what else is available, /// check out the [`AsBindGroup`] documentation. +/// /// ``` -/// # use bevy_sprite::{Material2d, MaterialMesh2dBundle}; +/// # use bevy_sprite::{Material2d, MeshMaterial2d}; /// # use bevy_ecs::prelude::*; /// # use bevy_reflect::TypePath; -/// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image}; +/// # use bevy_render::{mesh::{Mesh, Mesh2d}, render_resource::{AsBindGroup, ShaderRef}, texture::Image}; /// # use bevy_color::LinearRgba; +/// # use bevy_color::palettes::basic::RED; /// # use bevy_asset::{Handle, AssetServer, Assets, Asset}; -/// +/// # use bevy_math::primitives::Circle; +/// # /// #[derive(AsBindGroup, Debug, Clone, Asset, TypePath)] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -80,17 +87,23 @@ use crate::{ /// } /// } /// -/// // Spawn an entity using `CustomMaterial`. -/// fn setup(mut commands: Commands, mut materials: ResMut>, asset_server: Res) { -/// commands.spawn(MaterialMesh2dBundle { -/// material: materials.add(CustomMaterial { -/// color: LinearRgba::RED, +/// // Spawn an entity with a mesh using `CustomMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// asset_server: Res, +/// ) { +/// commands.spawn(( +/// Mesh2d(meshes.add(Circle::new(50.0))), +/// MeshMaterial2d(materials.add(CustomMaterial { +/// color: RED.into(), /// color_texture: asset_server.load("some_image.png"), -/// }), -/// ..Default::default() -/// }); +/// })), +/// )); /// } /// ``` +/// /// In WGSL shaders, the material's binding would look like this: /// /// ```wgsl @@ -137,8 +150,104 @@ pub trait Material2d: AsBindGroup + Asset + Clone + Sized { } } +/// A [material](Material2d) for a [`Mesh2d`]. +/// +/// See [`Material2d`] for general information about 2D materials and how to implement your own materials. +/// +/// # Example +/// +/// ``` +/// # use bevy_sprite::{ColorMaterial, MeshMaterial2d}; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::{Mesh, Mesh2d}; +/// # use bevy_color::palettes::basic::RED; +/// # use bevy_asset::Assets; +/// # use bevy_math::primitives::Circle; +/// # +/// // Spawn an entity with a mesh using `ColorMaterial`. +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// commands.spawn(( +/// Mesh2d(meshes.add(Circle::new(50.0))), +/// MeshMaterial2d(materials.add(ColorMaterial::from_color(RED))), +/// )); +/// } +/// ``` +/// +/// [`MeshMaterial2d`]: crate::MeshMaterial2d +/// [`ColorMaterial`]: crate::ColorMaterial +/// +/// ## Default Material +/// +/// Meshes without a [`MeshMaterial2d`] are rendered with a default [`ColorMaterial`]. +/// This material can be overridden by inserting a custom material for the default asset handle. +/// +/// ``` +/// # use bevy_sprite::ColorMaterial; +/// # use bevy_ecs::prelude::*; +/// # use bevy_render::mesh::{Mesh, Mesh2d}; +/// # use bevy_color::Color; +/// # use bevy_asset::{Assets, Handle}; +/// # use bevy_math::primitives::Circle; +/// # +/// fn setup( +/// mut commands: Commands, +/// mut meshes: ResMut>, +/// mut materials: ResMut>, +/// ) { +/// // Optional: Insert a custom default material. +/// materials.insert( +/// &Handle::::default(), +/// ColorMaterial::from(Color::srgb(1.0, 0.0, 1.0)), +/// ); +/// +/// // Spawn a circle with no material. +/// // The mesh will be rendered with the default material. +/// commands.spawn(Mesh2d(meshes.add(Circle::new(50.0)))); +/// } +/// ``` +#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq)] +#[reflect(Component, Default)] +#[require(HasMaterial2d)] +pub struct MeshMaterial2d(pub Handle); + +impl Default for MeshMaterial2d { + fn default() -> Self { + Self(Handle::default()) + } +} + +impl From> for MeshMaterial2d { + fn from(handle: Handle) -> Self { + Self(handle) + } +} + +impl From> for AssetId { + fn from(material: MeshMaterial2d) -> Self { + material.id() + } +} + +impl From<&MeshMaterial2d> for AssetId { + fn from(material: &MeshMaterial2d) -> Self { + material.id() + } +} + +/// A component that marks an entity as having a [`MeshMaterial2d`]. +/// [`Mesh2d`] entities without this component are rendered with a [default material]. +/// +/// [default material]: crate::MeshMaterial2d#default-material +#[derive(Component, Clone, Debug, Default, Reflect)] +#[reflect(Component, Default)] +pub struct HasMaterial2d; + /// Sets how a 2d material's base color alpha channel is used for transparency. -/// Currently, this only works with [`Mesh2d`](crate::mesh2d::Mesh2d). Sprites are always transparent. +/// Currently, this only works with [`Mesh2d`]. Sprites are always transparent. /// /// This is very similar to [`AlphaMode`](bevy_render::alpha::AlphaMode) but this only applies to 2d meshes. /// We use a separate type because 2d doesn't support all the transparency modes that 3d does. @@ -179,6 +288,8 @@ where { fn build(&self, app: &mut App) { app.init_asset::() + .register_type::>() + .register_type::() .add_plugins(RenderAssetPlugin::>::default()); if let Some(render_app) = app.get_sub_app_mut(RenderApp) { @@ -188,7 +299,14 @@ where .add_render_command::>() .init_resource::>() .init_resource::>>() - .add_systems(ExtractSchedule, extract_material_meshes_2d::) + .add_systems( + ExtractSchedule, + ( + clear_material_2d_instances::, + extract_mesh_materials_2d::, + ) + .chain(), + ) .add_systems( Render, queue_material2d_meshes:: @@ -214,14 +332,33 @@ impl Default for RenderMaterial2dInstances { } } -fn extract_material_meshes_2d( +pub(crate) fn clear_material_2d_instances( mut material_instances: ResMut>, - query: Extract)>>, ) { material_instances.clear(); - for (entity, view_visibility, handle) in &query { +} + +fn extract_mesh_materials_2d( + mut material_instances: ResMut>, + query: Extract), With>>, +) { + for (entity, view_visibility, material) in &query { + if view_visibility.get() { + material_instances.insert(entity, material.id()); + } + } +} + +/// Extracts default materials for 2D meshes with no [`MeshMaterial2d`]. +pub(crate) fn extract_default_materials_2d( + mut material_instances: ResMut>, + query: Extract, Without)>>, +) { + let default_material: AssetId = Handle::::default().id(); + + for (entity, view_visibility) in &query { if view_visibility.get() { - material_instances.insert(entity, handle.id()); + material_instances.insert(entity, default_material); } } } @@ -340,7 +477,7 @@ impl FromWorld for Material2dPipeline { } } -type DrawMaterial2d = ( +pub(super) type DrawMaterial2d = ( SetItemPipeline, SetMesh2dViewBindGroup<0>, SetMesh2dBindGroup<1>, @@ -460,7 +597,7 @@ pub fn queue_material2d_meshes( view_key |= Mesh2dPipelineKey::DEBAND_DITHER; } } - for visible_entity in visible_entities.iter::() { + for visible_entity in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; @@ -609,11 +746,15 @@ impl RenderAsset for PreparedMaterial2d { } } -/// A component bundle for entities with a [`Mesh2dHandle`] and a [`Material2d`]. +/// A component bundle for entities with a [`Mesh2d`] and a [`MeshMaterial2d`]. #[derive(Bundle, Clone)] +#[deprecated( + since = "0.15.0", + note = "Use the `Mesh2d` and `MeshMaterial2d` components instead. Inserting them will now also insert the other components required by them automatically." +)] pub struct MaterialMesh2dBundle { - pub mesh: Mesh2dHandle, - pub material: Handle, + pub mesh: Mesh2d, + pub material: MeshMaterial2d, pub transform: Transform, pub global_transform: GlobalTransform, /// User indication of whether an entity is visible diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index a0e6229a6af1f..b0b5f39bc9af8 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -15,7 +15,6 @@ use bevy_ecs::{ system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Affine3, Vec4}; -use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ batching::{ gpu_preprocessing::IndirectParameters, @@ -27,7 +26,8 @@ use bevy_render::{ }, globals::{GlobalsBuffer, GlobalsUniform}, mesh::{ - allocator::MeshAllocator, Mesh, MeshVertexBufferLayoutRef, RenderMesh, RenderMeshBufferInfo, + allocator::MeshAllocator, Mesh, Mesh2d, MeshVertexBufferLayoutRef, RenderMesh, + RenderMeshBufferInfo, }, render_asset::RenderAssets, render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass}, @@ -48,19 +48,6 @@ use nonmax::NonMaxU32; use crate::Material2dBindGroupId; -/// Component for rendering with meshes in the 2d pipeline, usually with a [2d material](crate::Material2d) such as [`ColorMaterial`](crate::ColorMaterial). -/// -/// It wraps a [`Handle`] to differentiate from the 3d pipelines which use the handles directly as components -#[derive(Default, Clone, Component, Debug, Reflect, PartialEq, Eq, Deref, DerefMut)] -#[reflect(Default, Component, Debug, PartialEq)] -pub struct Mesh2dHandle(pub Handle); - -impl From> for Mesh2dHandle { - fn from(handle: Handle) -> Self { - Self(handle) - } -} - #[derive(Default)] pub struct Mesh2dRenderPlugin; @@ -218,7 +205,7 @@ pub struct RenderMesh2dInstance { pub struct RenderMesh2dInstances(EntityHashMap); #[derive(Component)] -pub struct Mesh2d; +pub struct Mesh2dMarker; pub fn extract_mesh2d( mut render_mesh_instances: ResMut, @@ -227,7 +214,7 @@ pub fn extract_mesh2d( Entity, &ViewVisibility, &GlobalTransform, - &Mesh2dHandle, + &Mesh2d, Has, )>, >, diff --git a/crates/bevy_sprite/src/mesh2d/wireframe2d.rs b/crates/bevy_sprite/src/mesh2d/wireframe2d.rs index a8822c800e5ec..2a40839b9c507 100644 --- a/crates/bevy_sprite/src/mesh2d/wireframe2d.rs +++ b/crates/bevy_sprite/src/mesh2d/wireframe2d.rs @@ -1,4 +1,4 @@ -use crate::{Material2d, Material2dKey, Material2dPlugin, Mesh2dHandle}; +use crate::{Material2d, Material2dKey, Material2dPlugin, Mesh2d}; use bevy_app::{Plugin, Startup, Update}; use bevy_asset::{load_internal_asset, Asset, Assets, Handle}; use bevy_color::{Color, LinearRgba}; @@ -9,6 +9,8 @@ use bevy_render::{ render_resource::*, }; +use super::MeshMaterial2d; + pub const WIREFRAME_2D_SHADER_HANDLE: Handle = Handle::weak_from_u128(6920362697190520314); /// A [`Plugin`] that draws wireframes for 2D meshes. @@ -126,12 +128,12 @@ fn global_color_changed( fn wireframe_color_changed( mut materials: ResMut>, mut colors_changed: Query< - (&mut Handle, &Wireframe2dColor), + (&mut MeshMaterial2d, &Wireframe2dColor), (With, Changed), >, ) { for (mut handle, wireframe_color) in &mut colors_changed { - *handle = materials.add(Wireframe2dMaterial { + handle.0 = materials.add(Wireframe2dMaterial { color: wireframe_color.color.into(), }); } @@ -144,15 +146,24 @@ fn apply_wireframe_material( mut materials: ResMut>, wireframes: Query< (Entity, Option<&Wireframe2dColor>), - (With, Without>), + ( + With, + Without>, + ), + >, + no_wireframes: Query< + Entity, + ( + With, + With>, + ), >, - no_wireframes: Query, With>)>, mut removed_wireframes: RemovedComponents, global_material: Res, ) { for e in removed_wireframes.read().chain(no_wireframes.iter()) { if let Some(commands) = commands.get_entity(e) { - commands.remove::>(); + commands.remove::>(); } } @@ -166,16 +177,12 @@ fn apply_wireframe_material( // If there's no color specified we can use the global material since it's already set to use the default_color global_material.handle.clone() }; - wireframes_to_spawn.push((e, material)); + wireframes_to_spawn.push((e, MeshMaterial2d(material))); } commands.insert_or_spawn_batch(wireframes_to_spawn); } -type Wireframe2dFilter = ( - With, - Without, - Without, -); +type Wireframe2dFilter = (With, Without, Without); /// Applies or removes a wireframe material on any mesh without a [`Wireframe2d`] or [`NoWireframe2d`] component. fn apply_global_wireframe_material( @@ -183,11 +190,14 @@ fn apply_global_wireframe_material( config: Res, meshes_without_material: Query< Entity, - (Wireframe2dFilter, Without>), + ( + Wireframe2dFilter, + Without>, + ), >, meshes_with_global_material: Query< Entity, - (Wireframe2dFilter, With>), + (Wireframe2dFilter, With>), >, global_material: Res, ) { @@ -196,12 +206,14 @@ fn apply_global_wireframe_material( for e in &meshes_without_material { // We only add the material handle but not the Wireframe component // This makes it easy to detect which mesh is using the global material and which ones are user specified - material_to_spawn.push((e, global_material.handle.clone())); + material_to_spawn.push((e, MeshMaterial2d(global_material.handle.clone()))); } commands.insert_or_spawn_batch(material_to_spawn); } else { for e in &meshes_with_global_material { - commands.entity(e).remove::>(); + commands + .entity(e) + .remove::>(); } } } diff --git a/errors/B0004.md b/errors/B0004.md index 0d81c0b3cd4e8..4a658c7aa0184 100644 --- a/errors/B0004.md +++ b/errors/B0004.md @@ -32,12 +32,11 @@ fn setup_cube( .spawn(Transform::default()) .with_children(|parent| { // cube - parent.spawn(PbrBundle { - mesh: meshes.add(Cuboid::default()), - material: materials.add(Color::rgb(0.8, 0.7, 0.6)), - transform: Transform::from_xyz(0.0, 0.5, 0.0), - ..default() - }); + parent.spawn(( + Mesh3d(meshes.add(Cuboid::default())), + MeshMaterial3d(materials.add(Color::rgb(0.8, 0.7, 0.6))), + Transform::from_xyz(0.0, 0.5, 0.0), + )); }); // camera @@ -61,7 +60,8 @@ doesn't have a [`ViewVisibility`] or [`InheritedVisibility`] component. Since the cube is spawned as a child of an entity without the visibility components, it will not be visible at all. -To fix this, you must use [`SpatialBundle`], as follows: +To fix this, you must also add a [`Visibility`] component. +It automatically adds the other relevant visibility components for you: ```rust,no_run use bevy::prelude::*; @@ -72,18 +72,14 @@ fn setup_cube( mut materials: ResMut>, ) { commands - // We use SpatialBundle instead of Transform, it contains the - // visibility components needed to display the cube, - // In addition to the Transform and GlobalTransform components. - .spawn(SpatialBundle::default()) + .spawn((Transform::default(), Visibility::default())) .with_children(|parent| { // cube - parent.spawn(PbrBundle { - mesh: meshes.add(Cuboid::default()), - material: materials.add(Color::rgb(0.8, 0.7, 0.6)), - transform: Transform::from_xyz(0.0, 0.5, 0.0), - ..default() - }); + parent.spawn(( + Mesh3d(meshes.add(Cuboid::default())), + MeshMaterial3d(materials.add(Color::rgb(0.8, 0.7, 0.6))), + Transform::from_xyz(0.0, 0.5, 0.0), + )); }); // camera @@ -103,14 +99,14 @@ fn main() { A similar problem occurs when the [`GlobalTransform`] component is missing. However, it will be automatically inserted whenever `Transform` is -inserted, as it's a required component. +inserted, as it is a required component. You will most likely encounter this warning when loading a scene as a child of a pre-existing [`Entity`] that does not have the proper components. [`InheritedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.InheritedVisibility.html [`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.html +[`Visibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.Visibility.html [`GlobalTransform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.GlobalTransform.html [`Parent`]: https://docs.rs/bevy/*/bevy/hierarchy/struct.Parent.html [`Entity`]: https://docs.rs/bevy/*/bevy/ecs/entity/struct.Entity.html -[`SpatialBundle`]: https://docs.rs/bevy/*/bevy/render/prelude/struct.SpatialBundle.html diff --git a/examples/2d/2d_shapes.rs b/examples/2d/2d_shapes.rs index bd007d77df5db..2d59bbf56ccd8 100644 --- a/examples/2d/2d_shapes.rs +++ b/examples/2d/2d_shapes.rs @@ -3,12 +3,9 @@ //! You can toggle wireframes with the space bar except on wasm. Wasm does not support //! `POLYGON_MODE_LINE` on the gpu. +use bevy::prelude::*; #[cfg(not(target_arch = "wasm32"))] use bevy::sprite::{Wireframe2dConfig, Wireframe2dPlugin}; -use bevy::{ - prelude::*, - sprite::{MaterialMesh2dBundle, Mesh2dHandle}, -}; fn main() { let mut app = App::new(); @@ -33,20 +30,20 @@ fn setup( commands.spawn(Camera2dBundle::default()); let shapes = [ - Mesh2dHandle(meshes.add(Circle::new(50.0))), - Mesh2dHandle(meshes.add(CircularSector::new(50.0, 1.0))), - Mesh2dHandle(meshes.add(CircularSegment::new(50.0, 1.25))), - Mesh2dHandle(meshes.add(Ellipse::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Annulus::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Capsule2d::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Rhombus::new(75.0, 100.0))), - Mesh2dHandle(meshes.add(Rectangle::new(50.0, 100.0))), - Mesh2dHandle(meshes.add(RegularPolygon::new(50.0, 6))), - Mesh2dHandle(meshes.add(Triangle2d::new( + meshes.add(Circle::new(50.0)), + meshes.add(CircularSector::new(50.0, 1.0)), + meshes.add(CircularSegment::new(50.0, 1.25)), + meshes.add(Ellipse::new(25.0, 50.0)), + meshes.add(Annulus::new(25.0, 50.0)), + meshes.add(Capsule2d::new(25.0, 50.0)), + meshes.add(Rhombus::new(75.0, 100.0)), + meshes.add(Rectangle::new(50.0, 100.0)), + meshes.add(RegularPolygon::new(50.0, 6)), + meshes.add(Triangle2d::new( Vec2::Y * 50.0, Vec2::new(-50.0, -50.0), Vec2::new(50.0, -50.0), - ))), + )), ]; let num_shapes = shapes.len(); @@ -54,17 +51,16 @@ fn setup( // Distribute colors evenly across the rainbow. let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7); - commands.spawn(MaterialMesh2dBundle { - mesh: shape, - material: materials.add(color), - transform: Transform::from_xyz( + commands.spawn(( + Mesh2d(shape), + MeshMaterial2d(materials.add(color)), + Transform::from_xyz( // Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2. -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT, 0.0, 0.0, ), - ..default() - }); + )); } #[cfg(not(target_arch = "wasm32"))] diff --git a/examples/2d/bloom_2d.rs b/examples/2d/bloom_2d.rs index a603ced251bf9..dcffdcafcc10a 100644 --- a/examples/2d/bloom_2d.rs +++ b/examples/2d/bloom_2d.rs @@ -6,7 +6,6 @@ use bevy::{ tonemapping::Tonemapping, }, prelude::*, - sprite::MaterialMesh2dBundle, }; fn main() { @@ -47,22 +46,20 @@ fn setup( }); // Circle mesh - commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(Circle::new(100.)).into(), + commands.spawn(( + Mesh2d(meshes.add(Circle::new(100.))), // 4. Put something bright in a dark environment to see the effect - material: materials.add(Color::srgb(7.5, 0.0, 7.5)), - transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), - ..default() - }); + MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))), + Transform::from_translation(Vec3::new(-200., 0., 0.)), + )); // Hexagon mesh - commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(RegularPolygon::new(100., 6)).into(), + commands.spawn(( + Mesh2d(meshes.add(RegularPolygon::new(100., 6))), // 4. Put something bright in a dark environment to see the effect - material: materials.add(Color::srgb(6.25, 9.4, 9.1)), - transform: Transform::from_translation(Vec3::new(200., 0., 0.)), - ..default() - }); + MeshMaterial2d(materials.add(Color::srgb(6.25, 9.4, 9.1))), + Transform::from_translation(Vec3::new(200., 0., 0.)), + )); // UI commands.spawn( diff --git a/examples/2d/custom_gltf_vertex_attribute.rs b/examples/2d/custom_gltf_vertex_attribute.rs index c319e1b525ff0..80323cb6ab4e1 100644 --- a/examples/2d/custom_gltf_vertex_attribute.rs +++ b/examples/2d/custom_gltf_vertex_attribute.rs @@ -8,7 +8,7 @@ use bevy::{ mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef}, render_resource::*, }, - sprite::{Material2d, Material2dKey, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle}, + sprite::{Material2d, Material2dKey, Material2dPlugin}, }; /// This example uses a shader source file from the assets subdirectory @@ -53,12 +53,11 @@ fn setup( } .from_asset("models/barycentric/barycentric.gltf"), ); - commands.spawn(MaterialMesh2dBundle { - mesh: Mesh2dHandle(mesh), - material: materials.add(CustomMaterial {}), - transform: Transform::from_scale(150.0 * Vec3::ONE), - ..default() - }); + commands.spawn(( + Mesh2d(mesh), + MeshMaterial2d(materials.add(CustomMaterial {})), + Transform::from_scale(150.0 * Vec3::ONE), + )); // Add a camera commands.spawn(Camera2dBundle { ..default() }); diff --git a/examples/2d/mesh2d.rs b/examples/2d/mesh2d.rs index 51c2c6c5da2dd..2857a4d722d57 100644 --- a/examples/2d/mesh2d.rs +++ b/examples/2d/mesh2d.rs @@ -1,6 +1,6 @@ //! Shows how to render a polygonal [`Mesh`], generated from a [`Rectangle`] primitive, in a 2D scene. -use bevy::{color::palettes::basic::PURPLE, prelude::*, sprite::MaterialMesh2dBundle}; +use bevy::{color::palettes::basic::PURPLE, prelude::*}; fn main() { App::new() @@ -15,10 +15,9 @@ fn setup( mut materials: ResMut>, ) { commands.spawn(Camera2dBundle::default()); - commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(Rectangle::default()).into(), - transform: Transform::default().with_scale(Vec3::splat(128.)), - material: materials.add(Color::from(PURPLE)), - ..default() - }); + commands.spawn(( + Mesh2d(meshes.add(Rectangle::default())), + MeshMaterial2d(materials.add(Color::from(PURPLE))), + Transform::default().with_scale(Vec3::splat(128.)), + )); } diff --git a/examples/2d/mesh2d_alpha_mode.rs b/examples/2d/mesh2d_alpha_mode.rs index 0cb8b5bd03145..ce552fd7115b4 100644 --- a/examples/2d/mesh2d_alpha_mode.rs +++ b/examples/2d/mesh2d_alpha_mode.rs @@ -1,10 +1,10 @@ -//! This example is used to test how transforms interact with alpha modes for [`MaterialMesh2dBundle`] entities. +//! This example is used to test how transforms interact with alpha modes for [`Mesh2d`] entities with a [`MeshMaterial2d`]. //! This makes sure the depth buffer is correctly being used for opaque and transparent 2d meshes use bevy::{ color::palettes::css::{BLUE, GREEN, WHITE}, prelude::*, - sprite::{AlphaMode2d, MaterialMesh2dBundle}, + sprite::AlphaMode2d, }; fn main() { @@ -28,70 +28,64 @@ fn setup( // opaque // Each sprite should be square with the transparent parts being completely black // The blue sprite should be on top with the white and green one behind it - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: WHITE.into(), alpha_mode: AlphaMode2d::Opaque, texture: Some(texture_handle.clone()), - }), - transform: Transform::from_xyz(-400.0, 0.0, 0.0), - ..default() - }); - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + })), + Transform::from_xyz(-400.0, 0.0, 0.0), + )); + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: BLUE.into(), alpha_mode: AlphaMode2d::Opaque, texture: Some(texture_handle.clone()), - }), - transform: Transform::from_xyz(-300.0, 0.0, 1.0), - ..default() - }); - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + })), + Transform::from_xyz(-300.0, 0.0, 1.0), + )); + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: GREEN.into(), alpha_mode: AlphaMode2d::Opaque, texture: Some(texture_handle.clone()), - }), - transform: Transform::from_xyz(-200.0, 0.0, -1.0), - ..default() - }); + })), + Transform::from_xyz(-200.0, 0.0, -1.0), + )); // Test the interaction between opaque/mask and transparent meshes // The white sprite should be: // - only the icon is opaque but background is transparent // - on top of the green sprite // - behind the blue sprite - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: WHITE.into(), alpha_mode: AlphaMode2d::Mask(0.5), texture: Some(texture_handle.clone()), - }), - transform: Transform::from_xyz(200.0, 0.0, 0.0), - ..default() - }); - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + })), + Transform::from_xyz(200.0, 0.0, 0.0), + )); + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: BLUE.with_alpha(0.7).into(), alpha_mode: AlphaMode2d::Blend, texture: Some(texture_handle.clone()), - }), - transform: Transform::from_xyz(300.0, 0.0, 1.0), - ..default() - }); - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone().into(), - material: materials.add(ColorMaterial { + })), + Transform::from_xyz(300.0, 0.0, 1.0), + )); + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial { color: GREEN.with_alpha(0.7).into(), alpha_mode: AlphaMode2d::Blend, texture: Some(texture_handle), - }), - transform: Transform::from_xyz(400.0, 0.0, -1.0), - ..default() - }); + })), + Transform::from_xyz(400.0, 0.0, -1.0), + )); } diff --git a/examples/2d/mesh2d_arcs.rs b/examples/2d/mesh2d_arcs.rs index b9f63ffd5bc18..577c8398fffb3 100644 --- a/examples/2d/mesh2d_arcs.rs +++ b/examples/2d/mesh2d_arcs.rs @@ -12,7 +12,6 @@ use bevy::{ }, prelude::*, render::mesh::{CircularMeshUvMode, CircularSectorMeshBuilder, CircularSegmentMeshBuilder}, - sprite::MaterialMesh2dBundle, }; fn main() { @@ -68,14 +67,11 @@ fn setup( angle: sector_angle, }); commands.spawn(( - MaterialMesh2dBundle { - mesh: meshes.add(sector_mesh).into(), - material: material.clone(), - transform: Transform { - translation: Vec3::new(FIRST_X + OFFSET * i as f32, 2.0 * UPPER_Y, 0.0), - rotation: Quat::from_rotation_z(sector_angle), - ..default() - }, + Mesh2d(meshes.add(sector_mesh)), + MeshMaterial2d(material.clone()), + Transform { + translation: Vec3::new(FIRST_X + OFFSET * i as f32, 2.0 * UPPER_Y, 0.0), + rotation: Quat::from_rotation_z(sector_angle), ..default() }, DrawBounds(sector), @@ -95,14 +91,11 @@ fn setup( angle: -segment_angle, }); commands.spawn(( - MaterialMesh2dBundle { - mesh: meshes.add(segment_mesh).into(), - material: material.clone(), - transform: Transform { - translation: Vec3::new(FIRST_X + OFFSET * i as f32, LOWER_Y, 0.0), - rotation: Quat::from_rotation_z(segment_angle), - ..default() - }, + Mesh2d(meshes.add(segment_mesh)), + MeshMaterial2d(material.clone()), + Transform { + translation: Vec3::new(FIRST_X + OFFSET * i as f32, LOWER_Y, 0.0), + rotation: Quat::from_rotation_z(segment_angle), ..default() }, DrawBounds(segment), diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 35380ec3e28c4..0daf83d357d83 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -12,7 +12,7 @@ use bevy::{ math::{ops, FloatOrd}, prelude::*, render::{ - mesh::{Indices, MeshVertexAttribute, RenderMesh}, + mesh::{Indices, RenderMesh}, render_asset::{RenderAssetUsages, RenderAssets}, render_phase::{ AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline, @@ -30,9 +30,9 @@ use bevy::{ Extract, Render, RenderApp, RenderSet, }, sprite::{ - extract_mesh2d, DrawMesh2d, Material2dBindGroupId, Mesh2dHandle, Mesh2dPipeline, + extract_mesh2d, DrawMesh2d, HasMaterial2d, Material2dBindGroupId, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dTransforms, MeshFlags, RenderMesh2dInstance, SetMesh2dBindGroup, - SetMesh2dViewBindGroup, WithMesh2d, + SetMesh2dViewBindGroup, }, }; use std::f32::consts::PI; @@ -85,12 +85,9 @@ fn star( // Set the position attribute star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); // And a RGB color attribute as well - let mut v_color: Vec = vec![LinearRgba::BLACK.as_u32()]; - v_color.extend_from_slice(&[LinearRgba::from(YELLOW).as_u32(); 10]); - star.insert_attribute( - MeshVertexAttribute::new("Vertex_Color", 1, VertexFormat::Uint32), - v_color, - ); + let mut v_color: Vec<[f32; 4]> = vec![LinearRgba::BLACK.to_f32_array()]; + v_color.extend_from_slice(&[LinearRgba::from(YELLOW).to_f32_array(); 10]); + star.insert_attribute(Mesh::ATTRIBUTE_COLOR, v_color); // Now, we specify the indices of the vertex that are going to compose the // triangles in our star. Vertices in triangles have to be specified in CCW @@ -111,8 +108,8 @@ fn star( commands.spawn(( // We use a marker component to identify the custom colored meshes ColoredMesh2d, - // The `Handle` needs to be wrapped in a `Mesh2dHandle` to use 2d rendering instead of 3d - Mesh2dHandle(meshes.add(star)), + // The `Handle` needs to be wrapped in a `Mesh2d` for 2D rendering + Mesh2d(meshes.add(star)), // This bundle's components are needed for something to be rendered SpatialBundle::INHERITED_IDENTITY, )); @@ -121,8 +118,10 @@ fn star( commands.spawn(Camera2dBundle::default()); } +// Require `HasMaterial2d` to indicate that no placeholder material should be rendeed. /// A marker component for colored 2d meshes #[derive(Component, Default)] +#[require(HasMaterial2d)] pub struct ColoredMesh2d; /// Custom pipeline for 2d meshes with vertex colors @@ -330,7 +329,7 @@ pub fn extract_colored_mesh2d( // When extracting, you must use `Extract` to mark the `SystemParam`s // which should be taken from the main world. query: Extract< - Query<(Entity, &ViewVisibility, &GlobalTransform, &Mesh2dHandle), With>, + Query<(Entity, &ViewVisibility, &GlobalTransform, &Mesh2d), With>, >, mut render_mesh_instances: ResMut, ) { @@ -387,7 +386,7 @@ pub fn queue_colored_mesh2d( | Mesh2dPipelineKey::from_hdr(view.hdr); // Queue all entities visible to that view - for visible_entity in visible_entities.iter::() { + for visible_entity in visible_entities.iter::>() { if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) { let mesh2d_handle = mesh_instance.mesh_asset_id; let mesh2d_transforms = &mesh_instance.transforms; diff --git a/examples/2d/mesh2d_vertex_color_texture.rs b/examples/2d/mesh2d_vertex_color_texture.rs index 654c8e118d957..9fa00db852f80 100644 --- a/examples/2d/mesh2d_vertex_color_texture.rs +++ b/examples/2d/mesh2d_vertex_color_texture.rs @@ -1,10 +1,7 @@ //! Shows how to render a polygonal [`Mesh`], generated from a [`Rectangle`] primitive, in a 2D scene. //! Adds a texture and colored vertices, giving per-vertex tinting. -use bevy::{ - prelude::*, - sprite::{MaterialMesh2dBundle, Mesh2dHandle}, -}; +use bevy::prelude::*; fn main() { App::new() @@ -33,26 +30,22 @@ fn setup( // Insert the vertex colors as an attribute mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_colors); - let mesh_handle: Mesh2dHandle = meshes.add(mesh).into(); + let mesh_handle = meshes.add(mesh); // Spawn camera commands.spawn(Camera2dBundle::default()); // Spawn the quad with vertex colors - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle.clone(), - transform: Transform::from_translation(Vec3::new(-96., 0., 0.)) - .with_scale(Vec3::splat(128.)), - material: materials.add(ColorMaterial::default()), - ..default() - }); + commands.spawn(( + Mesh2d(mesh_handle.clone()), + MeshMaterial2d(materials.add(ColorMaterial::default())), + Transform::from_translation(Vec3::new(-96., 0., 0.)).with_scale(Vec3::splat(128.)), + )); // Spawning the quad with vertex colors and a texture results in tinting - commands.spawn(MaterialMesh2dBundle { - mesh: mesh_handle, - transform: Transform::from_translation(Vec3::new(96., 0., 0.)) - .with_scale(Vec3::splat(128.)), - material: materials.add(texture_handle), - ..default() - }); + commands.spawn(( + Mesh2d(mesh_handle), + MeshMaterial2d(materials.add(texture_handle)), + Transform::from_translation(Vec3::new(96., 0., 0.)).with_scale(Vec3::splat(128.)), + )); } diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 95c7079dddea4..5cac633a3cdcb 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -9,7 +9,6 @@ use bevy::{ }, view::RenderLayers, }, - sprite::MaterialMesh2dBundle, window::WindowResized, }; @@ -81,12 +80,9 @@ fn setup_mesh( mut materials: ResMut>, ) { commands.spawn(( - MaterialMesh2dBundle { - mesh: meshes.add(Capsule2d::default()).into(), - transform: Transform::from_xyz(40., 0., 2.).with_scale(Vec3::splat(32.)), - material: materials.add(Color::BLACK), - ..default() - }, + Mesh2d(meshes.add(Capsule2d::default())), + MeshMaterial2d(materials.add(Color::BLACK)), + Transform::from_xyz(40., 0., 2.).with_scale(Vec3::splat(32.)), Rotate, PIXEL_PERFECT_LAYERS, )); diff --git a/examples/2d/wireframe_2d.rs b/examples/2d/wireframe_2d.rs index 40052642d2e5a..b53b20ea1e6db 100644 --- a/examples/2d/wireframe_2d.rs +++ b/examples/2d/wireframe_2d.rs @@ -16,10 +16,7 @@ use bevy::{ settings::{RenderCreation, WgpuSettings}, RenderPlugin, }, - sprite::{ - MaterialMesh2dBundle, NoWireframe2d, Wireframe2d, Wireframe2dColor, Wireframe2dConfig, - Wireframe2dPlugin, - }, + sprite::{NoWireframe2d, Wireframe2d, Wireframe2dColor, Wireframe2dConfig, Wireframe2dPlugin}, }; fn main() { @@ -59,35 +56,26 @@ fn setup( ) { // Triangle: Never renders a wireframe commands.spawn(( - MaterialMesh2dBundle { - mesh: meshes - .add(Triangle2d::new( - Vec2::new(0.0, 50.0), - Vec2::new(-50.0, -50.0), - Vec2::new(50.0, -50.0), - )) - .into(), - material: materials.add(Color::BLACK), - transform: Transform::from_xyz(-150.0, 0.0, 0.0), - ..default() - }, + Mesh2d(meshes.add(Triangle2d::new( + Vec2::new(0.0, 50.0), + Vec2::new(-50.0, -50.0), + Vec2::new(50.0, -50.0), + ))), + MeshMaterial2d(materials.add(Color::BLACK)), + Transform::from_xyz(-150.0, 0.0, 0.0), NoWireframe2d, )); // Rectangle: Follows global wireframe setting - commands.spawn(MaterialMesh2dBundle { - mesh: meshes.add(Rectangle::new(100.0, 100.0)).into(), - material: materials.add(Color::BLACK), - transform: Transform::from_xyz(0.0, 0.0, 0.0), - ..default() - }); + commands.spawn(( + Mesh2d(meshes.add(Rectangle::new(100.0, 100.0))), + MeshMaterial2d(materials.add(Color::BLACK)), + Transform::from_xyz(0.0, 0.0, 0.0), + )); // Circle: Always renders a wireframe commands.spawn(( - MaterialMesh2dBundle { - mesh: meshes.add(Circle::new(50.0)).into(), - material: materials.add(Color::BLACK), - transform: Transform::from_xyz(150.0, 0.0, 0.0), - ..default() - }, + Mesh2d(meshes.add(Circle::new(50.0))), + MeshMaterial2d(materials.add(Color::BLACK)), + Transform::from_xyz(150.0, 0.0, 0.0), Wireframe2d, // This lets you configure the wireframe color of this entity. // If not set, this will use the color in `WireframeConfig` diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index 07d369f17a503..437a8ff1ed0b0 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -16,19 +16,17 @@ fn setup( mut materials: ResMut>, ) { // circular base - commands.spawn(PbrBundle { - mesh: meshes.add(Circle::new(4.0)), - material: materials.add(Color::WHITE), - transform: Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), - ..default() - }); + commands.spawn(( + Mesh3d(meshes.add(Circle::new(4.0))), + MeshMaterial3d(materials.add(Color::WHITE)), + Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), + )); // cube - commands.spawn(PbrBundle { - mesh: meshes.add(Cuboid::new(1.0, 1.0, 1.0)), - material: materials.add(Color::srgb_u8(124, 144, 255)), - transform: Transform::from_xyz(0.0, 0.5, 0.0), - ..default() - }); + commands.spawn(( + Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), + MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), + Transform::from_xyz(0.0, 0.5, 0.0), + )); // light commands.spawn(( PointLight { diff --git a/examples/3d/3d_shapes.rs b/examples/3d/3d_shapes.rs index c2365e31dbc71..eae6d81aed95c 100644 --- a/examples/3d/3d_shapes.rs +++ b/examples/3d/3d_shapes.rs @@ -81,17 +81,14 @@ fn setup( for (i, shape) in shapes.into_iter().enumerate() { commands.spawn(( - PbrBundle { - mesh: shape, - material: debug_material.clone(), - transform: Transform::from_xyz( - -SHAPES_X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * SHAPES_X_EXTENT, - 2.0, - Z_EXTENT / 2., - ) - .with_rotation(Quat::from_rotation_x(-PI / 4.)), - ..default() - }, + Mesh3d(shape), + MeshMaterial3d(debug_material.clone()), + Transform::from_xyz( + -SHAPES_X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * SHAPES_X_EXTENT, + 2.0, + Z_EXTENT / 2., + ) + .with_rotation(Quat::from_rotation_x(-PI / 4.)), Shape, )); } @@ -100,18 +97,15 @@ fn setup( for (i, shape) in extrusions.into_iter().enumerate() { commands.spawn(( - PbrBundle { - mesh: shape, - material: debug_material.clone(), - transform: Transform::from_xyz( - -EXTRUSION_X_EXTENT / 2. - + i as f32 / (num_extrusions - 1) as f32 * EXTRUSION_X_EXTENT, - 2.0, - -Z_EXTENT / 2., - ) - .with_rotation(Quat::from_rotation_x(-PI / 4.)), - ..default() - }, + Mesh3d(shape), + MeshMaterial3d(debug_material.clone()), + Transform::from_xyz( + -EXTRUSION_X_EXTENT / 2. + + i as f32 / (num_extrusions - 1) as f32 * EXTRUSION_X_EXTENT, + 2.0, + -Z_EXTENT / 2., + ) + .with_rotation(Quat::from_rotation_x(-PI / 4.)), Shape, )); } @@ -128,11 +122,10 @@ fn setup( )); // ground plane - commands.spawn(PbrBundle { - mesh: meshes.add(Plane3d::default().mesh().size(50.0, 50.0).subdivisions(10)), - material: materials.add(Color::from(SILVER)), - ..default() - }); + commands.spawn(( + Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0).subdivisions(10))), + MeshMaterial3d(materials.add(Color::from(SILVER))), + )); commands.spawn(Camera3dBundle { transform: Transform::from_xyz(0.0, 7., 14.0).looking_at(Vec3::new(0., 1., 0.), Vec3::Y), diff --git a/examples/3d/3d_viewport_to_world.rs b/examples/3d/3d_viewport_to_world.rs index 704352b87701c..db11844e7e90d 100644 --- a/examples/3d/3d_viewport_to_world.rs +++ b/examples/3d/3d_viewport_to_world.rs @@ -57,11 +57,8 @@ fn setup( ) { // plane commands.spawn(( - PbrBundle { - mesh: meshes.add(Plane3d::default().mesh().size(20., 20.)), - material: materials.add(Color::srgb(0.3, 0.5, 0.3)), - ..default() - }, + Mesh3d(meshes.add(Plane3d::default().mesh().size(20., 20.))), + MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))), Ground, )); diff --git a/examples/3d/animated_material.rs b/examples/3d/animated_material.rs index 131284568ddc5..53bb7163f745a 100644 --- a/examples/3d/animated_material.rs +++ b/examples/3d/animated_material.rs @@ -37,19 +37,18 @@ fn setup( let mut hsla = Hsla::hsl(0.0, 1.0, 0.5); for x in -1..2 { for z in -1..2 { - commands.spawn(PbrBundle { - mesh: cube.clone(), - material: materials.add(Color::from(hsla)), - transform: Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)), - ..default() - }); + commands.spawn(( + Mesh3d(cube.clone()), + MeshMaterial3d(materials.add(Color::from(hsla))), + Transform::from_translation(Vec3::new(x as f32, 0.0, z as f32)), + )); hsla = hsla.rotate_hue(GOLDEN_ANGLE); } } } fn animate_materials( - material_handles: Query<&Handle>, + material_handles: Query<&MeshMaterial3d>, time: Res