Skip to content

Commit 3da0ef0

Browse files
Remove the Component trait implementation from Handle (#15796)
# Objective - Closes #15716 - Closes #15718 ## Solution - Replace `Handle<MeshletMesh>` with a new `MeshletMesh3d` component - As expected there were some random things that needed fixing: - A couple tests were storing handles just to prevent them from being dropped I believe, which seems to have been unnecessary in some. - The `SpriteBundle` still had a `Handle<Image>` field. I've removed this. - Tests in `bevy_sprite` incorrectly added a `Handle<Image>` field outside of the `Sprite` component. - A few examples were still inserting `Handle`s, switched those to their corresponding wrappers. - 2 examples that were still querying for `Handle<Image>` were changed to query `Sprite` ## Testing - I've verified that the changed example work now ## Migration Guide `Handle` can no longer be used as a `Component`. All existing Bevy types using this pattern have been wrapped in their own semantically meaningful type. You should do the same for any custom `Handle` components your project needs. The `Handle<MeshletMesh>` component is now `MeshletMesh3d`. The `WithMeshletMesh` type alias has been removed. Use `With<MeshletMesh3d>` instead.
1 parent a6be9b4 commit 3da0ef0

File tree

17 files changed

+87
-79
lines changed

17 files changed

+87
-79
lines changed

crates/bevy_animation/src/animation_event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub(crate) fn trigger_animation_event(
5858
/// let mut player = AnimationPlayer::default();
5959
/// player.play(animation_index).repeat();
6060
///
61-
/// commands.spawn((graphs.add(graph), player));
61+
/// commands.spawn((AnimationGraphHandle(graphs.add(graph)), player));
6262
/// }
6363
/// #
6464
/// # bevy_ecs::system::assert_is_system(setup_animation);

crates/bevy_asset/src/handle.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::{
33
UntypedAssetId,
44
};
55
use alloc::sync::Arc;
6-
use bevy_ecs::prelude::*;
76
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
87
use core::{
98
any::TypeId,
@@ -122,8 +121,8 @@ impl core::fmt::Debug for StrongHandle {
122121
/// of the [`Handle`] are dropped.
123122
///
124123
/// [`Handle::Strong`] also provides access to useful [`Asset`] metadata, such as the [`AssetPath`] (if it exists).
125-
#[derive(Component, Reflect)]
126-
#[reflect(Default, Component, Debug, Hash, PartialEq)]
124+
#[derive(Reflect)]
125+
#[reflect(Default, Debug, Hash, PartialEq)]
127126
pub enum Handle<A: Asset> {
128127
/// A "strong" reference to a live (or loading) [`Asset`]. If a [`Handle`] is [`Handle::Strong`], the [`Asset`] will be kept
129128
/// alive until the [`Handle`] is dropped. Strong handles also provide access to additional asset metadata.

crates/bevy_asset/src/lib.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,6 @@ mod tests {
900900
let asset_server = app.world().resource::<AssetServer>().clone();
901901
let handle: Handle<CoolText> = asset_server.load(a_path);
902902
let a_id = handle.id();
903-
let entity = app.world_mut().spawn(handle).id();
904903
app.update();
905904
{
906905
let a_text = get::<CoolText>(app.world(), a_id);
@@ -1090,7 +1089,8 @@ mod tests {
10901089
a.text = "Changed".to_string();
10911090
}
10921091

1093-
app.world_mut().despawn(entity);
1092+
drop(handle);
1093+
10941094
app.update();
10951095
assert_eq!(
10961096
app.world().resource::<Assets<CoolText>>().len(),
@@ -1225,7 +1225,6 @@ mod tests {
12251225
);
12261226
}
12271227

1228-
app.world_mut().spawn(handle);
12291228
gate_opener.open(a_path);
12301229
gate_opener.open(b_path);
12311230
gate_opener.open(c_path);
@@ -1345,7 +1344,6 @@ mod tests {
13451344
let asset_server = app.world().resource::<AssetServer>().clone();
13461345
let handle: Handle<CoolText> = asset_server.load(a_path);
13471346
let a_id = handle.id();
1348-
app.world_mut().spawn(handle);
13491347

13501348
gate_opener.open(a_path);
13511349
run_app_until(&mut app, |world| {
@@ -1746,8 +1744,6 @@ mod tests {
17461744
let a_handle: Handle<CoolText> = asset_server.load(a_path);
17471745
let a_id = a_handle.id();
17481746

1749-
app.world_mut().spawn(a_handle);
1750-
17511747
run_app_until(&mut app, |world| {
17521748
let tracker = world.resource::<ErrorTracker>();
17531749
match tracker.finished_asset {

crates/bevy_gltf/src/loader.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,7 +2229,7 @@ mod test {
22292229
AssetApp, AssetPlugin, AssetServer, Assets, Handle, LoadState,
22302230
};
22312231
use bevy_core::TaskPoolPlugin;
2232-
use bevy_ecs::world::World;
2232+
use bevy_ecs::{system::Resource, world::World};
22332233
use bevy_log::LogPlugin;
22342234
use bevy_render::mesh::{skinning::SkinnedMeshInverseBindposes, MeshPlugin};
22352235
use bevy_scene::ScenePlugin;
@@ -2270,14 +2270,18 @@ mod test {
22702270
}
22712271

22722272
fn load_gltf_into_app(gltf_path: &str, gltf: &str) -> App {
2273+
#[expect(unused)]
2274+
#[derive(Resource)]
2275+
struct GltfHandle(Handle<Gltf>);
2276+
22732277
let dir = Dir::default();
22742278
dir.insert_asset_text(Path::new(gltf_path), gltf);
22752279
let mut app = test_app(dir);
22762280
app.update();
22772281
let asset_server = app.world().resource::<AssetServer>().clone();
22782282
let handle: Handle<Gltf> = asset_server.load(gltf_path.to_string());
22792283
let handle_id = handle.id();
2280-
app.world_mut().spawn(handle.clone());
2284+
app.insert_resource(GltfHandle(handle));
22812285
app.update();
22822286
run_app_until(&mut app, |_world| {
22832287
let load_state = asset_server.get_load_state(handle_id).unwrap();
@@ -2509,7 +2513,6 @@ mod test {
25092513
let asset_server = app.world().resource::<AssetServer>().clone();
25102514
let handle: Handle<Gltf> = asset_server.load(gltf_path);
25112515
let handle_id = handle.id();
2512-
app.world_mut().spawn(handle.clone());
25132516
app.update();
25142517
run_app_until(&mut app, |_world| {
25152518
let load_state = asset_server.get_load_state(handle_id).unwrap();
@@ -2552,7 +2555,6 @@ mod test {
25522555
let asset_server = app.world().resource::<AssetServer>().clone();
25532556
let handle: Handle<Gltf> = asset_server.load(gltf_path);
25542557
let handle_id = handle.id();
2555-
app.world_mut().spawn(handle.clone());
25562558
app.update();
25572559
run_app_until(&mut app, |_world| {
25582560
let load_state = asset_server.get_load_state(handle_id).unwrap();

crates/bevy_pbr/src/meshlet/asset.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub const MESHLET_MESH_ASSET_VERSION: u64 = 1;
3636
/// * Materials must use the [`crate::Material::meshlet_mesh_fragment_shader`] method (and similar variants for prepass/deferred shaders)
3737
/// which requires certain shader patterns that differ from the regular material shaders.
3838
///
39-
/// See also [`super::MaterialMeshletMeshBundle`] and [`super::MeshletPlugin`].
39+
/// See also [`super::MeshletMesh3d`] and [`super::MeshletPlugin`].
4040
#[derive(Asset, TypePath, Clone)]
4141
pub struct MeshletMesh {
4242
/// Quantized and bitstream-packed vertex positions for meshlet vertices.

crates/bevy_pbr/src/meshlet/instance_manager.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh};
1+
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d};
22
use crate::{
33
Material, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster, NotShadowReceiver,
44
PreviousGlobalTransform, RenderMaterialInstances,
55
};
6-
use bevy_asset::{AssetEvent, AssetServer, Assets, Handle, UntypedAssetId};
6+
use bevy_asset::{AssetEvent, AssetServer, Assets, UntypedAssetId};
77
use bevy_ecs::{
88
entity::{Entities, Entity, EntityHashMap},
99
event::EventReader,
@@ -168,7 +168,7 @@ pub fn extract_meshlet_mesh_entities(
168168
SystemState<(
169169
Query<(
170170
Entity,
171-
&Handle<MeshletMesh>,
171+
&MeshletMesh3d,
172172
&GlobalTransform,
173173
Option<&PreviousGlobalTransform>,
174174
Option<&RenderLayers>,

crates/bevy_pbr/src/meshlet/mod.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![expect(deprecated)]
12
//! Render high-poly 3d meshes using an efficient GPU-driven method. See [`MeshletPlugin`] and [`MeshletMesh`] for details.
23
34
mod asset;
@@ -57,19 +58,23 @@ use self::{
5758
};
5859
use crate::{graph::NodePbr, Material, MeshMaterial3d};
5960
use bevy_app::{App, Plugin, PostUpdate};
60-
use bevy_asset::{load_internal_asset, AssetApp, Handle};
61+
use bevy_asset::{load_internal_asset, AssetApp, AssetId, Handle};
6162
use bevy_core_pipeline::{
6263
core_3d::graph::{Core3d, Node3d},
6364
prepass::{DeferredPrepass, MotionVectorPrepass, NormalPrepass},
6465
};
66+
use bevy_derive::{Deref, DerefMut};
6567
use bevy_ecs::{
6668
bundle::Bundle,
69+
component::Component,
6770
entity::Entity,
6871
prelude::With,
6972
query::Has,
73+
reflect::ReflectComponent,
7074
schedule::IntoSystemConfigs,
7175
system::{Commands, Query},
7276
};
77+
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
7378
use bevy_render::{
7479
render_graph::{RenderGraphApp, ViewNodeRunner},
7580
render_resource::Shader,
@@ -83,6 +88,7 @@ use bevy_render::{
8388
};
8489
use bevy_transform::components::{GlobalTransform, Transform};
8590
use bevy_utils::tracing::error;
91+
use derive_more::From;
8692

8793
const MESHLET_BINDINGS_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(1325134235233421);
8894
const MESHLET_MESH_MATERIAL_SHADER_HANDLE: Handle<Shader> =
@@ -206,7 +212,7 @@ impl Plugin for MeshletPlugin {
206212
.register_asset_loader(MeshletMeshLoader)
207213
.add_systems(
208214
PostUpdate,
209-
check_visibility::<WithMeshletMesh>.in_set(VisibilitySystems::CheckVisibility),
215+
check_visibility::<With<MeshletMesh3d>>.in_set(VisibilitySystems::CheckVisibility),
210216
);
211217
}
212218

@@ -284,10 +290,31 @@ impl Plugin for MeshletPlugin {
284290
}
285291
}
286292

293+
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
294+
#[reflect(Component, Default)]
295+
#[require(Transform, Visibility)]
296+
pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
297+
298+
impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
299+
fn from(mesh: MeshletMesh3d) -> Self {
300+
mesh.id()
301+
}
302+
}
303+
304+
impl From<&MeshletMesh3d> for AssetId<MeshletMesh> {
305+
fn from(mesh: &MeshletMesh3d) -> Self {
306+
mesh.id()
307+
}
308+
}
309+
287310
/// A component bundle for entities with a [`MeshletMesh`] and a [`Material`].
288311
#[derive(Bundle, Clone)]
312+
#[deprecated(
313+
since = "0.15.0",
314+
note = "Use the `MeshletMesh3d` and `MeshMaterial3d` components instead. Inserting them will now also insert the other components required by them automatically."
315+
)]
289316
pub struct MaterialMeshletMeshBundle<M: Material> {
290-
pub meshlet_mesh: Handle<MeshletMesh>,
317+
pub meshlet_mesh: MeshletMesh3d,
291318
pub material: MeshMaterial3d<M>,
292319
pub transform: Transform,
293320
pub global_transform: GlobalTransform,
@@ -313,10 +340,6 @@ impl<M: Material> Default for MaterialMeshletMeshBundle<M> {
313340
}
314341
}
315342

316-
/// A convenient alias for `With<Handle<MeshletMesh>>`, for use with
317-
/// [`bevy_render::view::VisibleEntities`].
318-
pub type WithMeshletMesh = With<Handle<MeshletMesh>>;
319-
320343
fn configure_meshlet_views(
321344
mut views_3d: Query<(
322345
Entity,

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use bevy_utils::tracing::error;
3535
#[cfg(feature = "meshlet")]
3636
use crate::meshlet::{
3737
prepare_material_meshlet_meshes_prepass, queue_material_meshlet_meshes, InstanceManager,
38-
MeshletMesh,
38+
MeshletMesh3d,
3939
};
4040
use crate::*;
4141

@@ -221,7 +221,7 @@ pub struct PreviousGlobalTransform(pub Affine3A);
221221
#[cfg(not(feature = "meshlet"))]
222222
type PreviousMeshFilter = With<Mesh3d>;
223223
#[cfg(feature = "meshlet")]
224-
type PreviousMeshFilter = Or<(With<Mesh3d>, With<Handle<MeshletMesh>>)>;
224+
type PreviousMeshFilter = Or<(With<Mesh3d>, With<MeshletMesh3d>)>;
225225

226226
pub fn update_mesh_previous_global_transforms(
227227
mut commands: Commands,

crates/bevy_sprite/src/bundle.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
#![expect(deprecated)]
22
use crate::Sprite;
3-
use bevy_asset::Handle;
43
use bevy_ecs::bundle::Bundle;
54
use bevy_render::{
65
sync_world::SyncToRenderWorld,
7-
texture::Image,
86
view::{InheritedVisibility, ViewVisibility, Visibility},
97
};
108
use bevy_transform::components::{GlobalTransform, Transform};
@@ -28,8 +26,6 @@ pub struct SpriteBundle {
2826
pub transform: Transform,
2927
/// The absolute transform of the sprite. This should generally not be written to directly.
3028
pub global_transform: GlobalTransform,
31-
/// A reference-counted handle to the image asset to be drawn.
32-
pub texture: Handle<Image>,
3329
/// User indication of whether an entity is visible
3430
pub visibility: Visibility,
3531
/// Inherited visibility of an entity.

crates/bevy_sprite/src/lib.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,11 @@ mod test {
299299
// Add entities
300300
let entity = app
301301
.world_mut()
302-
.spawn((
303-
Sprite {
304-
custom_size: Some(Vec2::ZERO),
305-
..default()
306-
},
307-
image_handle,
308-
))
302+
.spawn(Sprite {
303+
custom_size: Some(Vec2::ZERO),
304+
image: image_handle,
305+
..default()
306+
})
309307
.id();
310308

311309
// Create initial AABB
@@ -364,14 +362,12 @@ mod test {
364362
// Add entities
365363
let entity = app
366364
.world_mut()
367-
.spawn((
368-
Sprite {
369-
rect: Some(Rect::new(0., 0., 0.5, 1.)),
370-
anchor: Anchor::TopRight,
371-
..default()
372-
},
373-
image_handle,
374-
))
365+
.spawn(Sprite {
366+
rect: Some(Rect::new(0., 0., 0.5, 1.)),
367+
anchor: Anchor::TopRight,
368+
image: image_handle,
369+
..default()
370+
})
375371
.id();
376372

377373
// Create AABB

examples/3d/clearcoat.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fn spawn_car_paint_sphere(
9898
commands
9999
.spawn((
100100
Mesh3d(sphere.clone()),
101-
materials.add(StandardMaterial {
101+
MeshMaterial3d(materials.add(StandardMaterial {
102102
clearcoat: 1.0,
103103
clearcoat_perceptual_roughness: 0.1,
104104
normal_map_texture: Some(asset_server.load_with_settings(
@@ -109,7 +109,7 @@ fn spawn_car_paint_sphere(
109109
perceptual_roughness: 0.5,
110110
base_color: BLUE.into(),
111111
..default()
112-
}),
112+
})),
113113
Transform::from_xyz(-1.0, 1.0, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
114114
))
115115
.insert(ExampleSphere);

examples/3d/meshlet.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod camera_controller;
77

88
use bevy::{
99
pbr::{
10-
experimental::meshlet::{MaterialMeshletMeshBundle, MeshletPlugin},
10+
experimental::meshlet::{MeshletMesh3d, MeshletPlugin},
1111
CascadeShadowConfigBuilder, DirectionalLightShadowMap,
1212
},
1313
prelude::*,
@@ -84,9 +84,9 @@ fn setup(
8484
let debug_material = debug_materials.add(MeshletDebugMaterial::default());
8585

8686
for x in -2..=2 {
87-
commands.spawn(MaterialMeshletMeshBundle {
88-
meshlet_mesh: meshlet_mesh_handle.clone(),
89-
material: MeshMaterial3d(standard_materials.add(StandardMaterial {
87+
commands.spawn((
88+
MeshletMesh3d(meshlet_mesh_handle.clone()),
89+
MeshMaterial3d(standard_materials.add(StandardMaterial {
9090
base_color: match x {
9191
-2 => Srgba::hex("#dc2626").unwrap().into(),
9292
-1 => Srgba::hex("#ea580c").unwrap().into(),
@@ -98,22 +98,20 @@ fn setup(
9898
perceptual_roughness: (x + 2) as f32 / 4.0,
9999
..default()
100100
})),
101-
transform: Transform::default()
101+
Transform::default()
102102
.with_scale(Vec3::splat(0.2))
103103
.with_translation(Vec3::new(x as f32 / 2.0, 0.0, -0.3)),
104-
..default()
105-
});
104+
));
106105
}
107106
for x in -2..=2 {
108-
commands.spawn(MaterialMeshletMeshBundle {
109-
meshlet_mesh: meshlet_mesh_handle.clone(),
110-
material: debug_material.clone().into(),
111-
transform: Transform::default()
107+
commands.spawn((
108+
MeshletMesh3d(meshlet_mesh_handle.clone()),
109+
MeshMaterial3d(debug_material.clone()),
110+
Transform::default()
112111
.with_scale(Vec3::splat(0.2))
113112
.with_rotation(Quat::from_rotation_y(PI))
114113
.with_translation(Vec3::new(x as f32 / 2.0, 0.0, 0.3)),
115-
..default()
116-
});
114+
));
117115
}
118116

119117
commands.spawn((

examples/animation/eased_motion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ fn setup(
4343
Transform::from_translation(vec3(-6., 2., 0.)),
4444
animation_target_name,
4545
animation_player,
46-
animation_graph,
46+
AnimationGraphHandle(animation_graph),
4747
))
4848
.id();
4949

0 commit comments

Comments
 (0)