Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ categories = ["game-engines", "rendering"]
resolver = "2"

[dependencies]
bevy = { version = "0.9", default-features = false, features = ["render"] }
bevy = { version = "0.10.0", default-features = false, features = [
"bevy_render",
"bevy_ui",
] }

bevy_mod_raycast = "0.7"
bevy_mod_raycast = "0.8"

[dev-dependencies]
bevy = { version = "0.9", default-features = false, features = [
bevy = { version = "0.10.0", default-features = false, features = [
"bevy_pbr",
"bevy_winit",
"bevy_ui",
Expand Down
6 changes: 3 additions & 3 deletions examples/deselection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use bevy_mod_picking::{DefaultPickingPlugins, NoDeselect, PickableBundle, Pickin
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
window: WindowDescriptor {
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync, // Reduce input latency
..default()
},
}),
..default()
}))
.add_plugins(DefaultPickingPlugins) // <- Adds picking, interaction, and highlighting
Expand All @@ -26,7 +26,7 @@ fn setup(
// plane
commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..Default::default()
},
Expand Down
4 changes: 2 additions & 2 deletions examples/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn main() {
.add_plugins(DefaultPlugins)
.add_plugins(DefaultPickingPlugins) // <- Adds picking, interaction, and highlighting
.add_startup_system(setup)
.add_system_to_stage(CoreStage::PostUpdate, print_events)
.add_system(print_events.in_base_set(CoreSet::PostUpdate))
.run();
}

Expand All @@ -28,7 +28,7 @@ fn setup(
) {
commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..Default::default()
},
Expand Down
6 changes: 3 additions & 3 deletions examples/minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use bevy_mod_picking::{
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
window: WindowDescriptor {
primary_window: Some(Window {
present_mode: PresentMode::AutoNoVsync, // Reduce input latency
..default()
},
}),
..default()
}))
.add_plugins(DefaultPickingPlugins) // <- Adds picking, interaction, and highlighting
Expand All @@ -29,7 +29,7 @@ fn setup(
) {
commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
mesh: meshes.add(Mesh::from(shape::Plane::from_size(5.0))),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..Default::default()
},
Expand Down
18 changes: 10 additions & 8 deletions examples/stress_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ use bevy_mod_picking::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
window: WindowDescriptor {
primary_window: Some(Window {
title: "bevy_mod_picking stress test".to_string(),
width: 800.,
height: 600.,
resolution: (800., 600.).into(),
present_mode: PresentMode::AutoNoVsync, // Reduce input latency
..default()
},
}),
..default()
}))
.add_plugins(DefaultPickingPlugins) // <- Adds picking, interaction, and highlighting
Expand All @@ -36,10 +35,13 @@ fn setup(
let tris_total = tris_sphere * (half_width as usize * 2).pow(3);
info!("Total tris: {}, Tris per mesh: {}", tris_total, tris_sphere);

let mesh_handle = meshes.add(Mesh::from(shape::Icosphere {
radius: 0.2,
subdivisions,
}));
let mesh_handle = meshes.add(
Mesh::try_from(shape::Icosphere {
radius: 0.2,
subdivisions,
})
.unwrap(),
);

let matl_handle = materials.add(StandardMaterial {
perceptual_roughness: 0.5,
Expand Down
18 changes: 6 additions & 12 deletions src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,12 @@ pub fn mesh_events_system(
mouse_button_input: Res<Input<MouseButton>>,
touches_input: Res<Touches>,
mut picking_events: EventWriter<PickingEvent>,
hover_query: Query<
(Entity, &Hover, ChangeTrackers<Hover>),
(Changed<Hover>, With<PickableMesh>),
>,
selection_query: Query<
(Entity, &Selection, ChangeTrackers<Selection>),
(Changed<Selection>, With<PickableMesh>),
>,
hover_query: Query<(Entity, Ref<Hover>), (Changed<Hover>, With<PickableMesh>)>,
selection_query: Query<(Entity, Ref<Selection>), (Changed<Selection>, With<PickableMesh>)>,
click_query: Query<(Entity, &Hover)>,
) {
for (entity, hover, hover_change) in hover_query.iter() {
if hover_change.is_added() {
for (entity, hover) in hover_query.iter() {
if hover.is_added() {
continue; // Avoid a false change detection when a component is added.
}
if hover.hovered() {
Expand All @@ -49,8 +43,8 @@ pub fn mesh_events_system(
picking_events.send(PickingEvent::Hover(HoverEvent::JustLeft(entity)));
}
}
for (entity, selection, selection_change) in selection_query.iter() {
if selection_change.is_added() {
for (entity, selection) in selection_query.iter() {
if selection.is_added() {
continue; // Avoid a false change detection when a component is added.
}
if selection.selected() {
Expand Down
157 changes: 72 additions & 85 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ pub use crate::{
};
pub use bevy_mod_raycast::{Primitive3d, RaycastMesh, RaycastSource};

use bevy::{
app::PluginGroupBuilder, asset::Asset, ecs::schedule::ShouldRun, prelude::*, ui::FocusPolicy,
};
use bevy::{app::PluginGroupBuilder, asset::Asset, prelude::*, ui::FocusPolicy};
use highlight::{get_initial_mesh_highlight_asset, Highlight};

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum PickingSystem {
UpdatePickSourcePositions,
BuildRays,
Expand All @@ -29,6 +27,9 @@ pub enum PickingSystem {
PauseForBlockers,
Focus,
Events,
PickingSet,
InteractableSet,
CustomHighlightSet,
}

/// A type alias for the concrete [RaycastMesh](bevy_mod_raycast::RaycastMesh) type used for Picking.
Expand All @@ -41,6 +42,7 @@ pub type PickingCamera = RaycastSource<PickingRaycastSet>;
/// meshes or ray sources that are being used by the picking plugin can be used by other ray
/// casting systems because they will have distinct types, e.g.: `RaycastMesh<PickingRaycastSet>`
/// vs. `RaycastMesh<MySuperCoolRaycastingType>`, and as such wil not result in collisions.
#[derive(Clone, Reflect)]
pub struct PickingRaycastSet;

#[derive(Clone, Debug, Resource)]
Expand All @@ -60,14 +62,6 @@ impl Default for PickingPluginsState {
}
}

fn simple_criteria(flag: bool) -> ShouldRun {
if flag {
ShouldRun::Yes
} else {
ShouldRun::No
}
}

#[derive(Default, Resource)]
pub struct PausedForBlockers(pub(crate) bool);
impl PausedForBlockers {
Expand Down Expand Up @@ -115,31 +109,23 @@ pub struct PickingPlugin;
impl Plugin for PickingPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<PickingPluginsState>()
.add_system_set_to_stage(
CoreStage::PreUpdate,
SystemSet::new()
.with_run_criteria(|state: Res<PickingPluginsState>| {
simple_criteria(state.enable_picking)
})
.with_system(
update_pick_source_positions
.label(PickingSystem::UpdatePickSourcePositions)
.before(PickingSystem::BuildRays),
)
.with_system(
bevy_mod_raycast::build_rays::<PickingRaycastSet>
.label(PickingSystem::BuildRays)
.before(PickingSystem::UpdateRaycast),
)
.with_system(
bevy_mod_raycast::update_raycast::<PickingRaycastSet>
.label(PickingSystem::UpdateRaycast)
.before(PickingSystem::UpdateIntersections),
)
.with_system(
bevy_mod_raycast::update_intersections::<PickingRaycastSet>
.label(PickingSystem::UpdateIntersections),
),
.add_systems(
(
update_pick_source_positions.in_set(PickingSystem::UpdatePickSourcePositions),
bevy_mod_raycast::build_rays::<PickingRaycastSet>
.in_set(PickingSystem::BuildRays),
bevy_mod_raycast::update_raycast::<PickingRaycastSet>
.in_set(PickingSystem::UpdateRaycast),
bevy_mod_raycast::update_intersections::<PickingRaycastSet>
.in_set(PickingSystem::UpdateIntersections),
)
.chain()
.in_set(PickingSystem::PickingSet),
)
.configure_set(
PickingSystem::PickingSet
.run_if(|state: Res<PickingPluginsState>| state.enable_picking)
.in_base_set(CoreSet::PreUpdate),
);
}
}
Expand All @@ -149,32 +135,21 @@ impl Plugin for InteractablePickingPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<PausedForBlockers>()
.add_event::<PickingEvent>()
.add_system_set_to_stage(
CoreStage::PreUpdate,
SystemSet::new()
.with_run_criteria(|state: Res<PickingPluginsState>| {
simple_criteria(state.enable_interacting)
})
.with_system(
pause_for_picking_blockers
.label(PickingSystem::PauseForBlockers)
.after(PickingSystem::UpdateIntersections),
)
.with_system(
mesh_focus
.label(PickingSystem::Focus)
.after(PickingSystem::PauseForBlockers),
)
.with_system(
mesh_selection
.label(PickingSystem::Selection)
.after(PickingSystem::Focus),
)
.with_system(
mesh_events_system
.label(PickingSystem::Events)
.after(PickingSystem::Selection),
),
.add_systems(
(
pause_for_picking_blockers.in_set(PickingSystem::PauseForBlockers),
mesh_focus.in_set(PickingSystem::Focus),
mesh_selection.in_set(PickingSystem::Selection),
mesh_events_system.in_set(PickingSystem::Events),
)
.chain()
.in_set(PickingSystem::InteractableSet),
)
.configure_set(
PickingSystem::InteractableSet
.run_if(|state: Res<PickingPluginsState>| state.enable_interacting)
.in_base_set(CoreSet::PreUpdate)
.after(PickingSystem::UpdateIntersections),
);
}
}
Expand All @@ -194,32 +169,30 @@ where
app.add_startup_system(move |mut commands: Commands, assets: ResMut<Assets<T>>| {
commands.insert_resource(highlighting_default(assets));
})
.add_system_set_to_stage(
CoreStage::PreUpdate,
SystemSet::new()
.with_run_criteria(|state: Res<PickingPluginsState>| {
simple_criteria(state.enable_highlighting)
})
.with_system(
get_initial_mesh_highlight_asset::<T>
.after(PickingSystem::UpdateIntersections)
.before(PickingSystem::Highlighting),
)
.with_system(
mesh_highlighting::<T>
.label(PickingSystem::Highlighting)
.before(PickingSystem::Events),
),
.add_systems(
(
get_initial_mesh_highlight_asset::<T>,
mesh_highlighting::<T>.in_set(PickingSystem::Highlighting),
)
.chain()
.in_set(PickingSystem::CustomHighlightSet),
)
.configure_set(
PickingSystem::CustomHighlightSet
.run_if(|state: Res<PickingPluginsState>| state.enable_highlighting)
.in_base_set(CoreSet::PreUpdate)
.before(PickingSystem::Events)
.after(PickingSystem::UpdateIntersections),
);
}
}

pub struct DebugCursorPickingPlugin;
impl Plugin for DebugCursorPickingPlugin {
fn build(&self, app: &mut App) {
app.add_system_to_stage(
CoreStage::PreUpdate,
app.add_system(
bevy_mod_raycast::update_debug_cursor::<PickingRaycastSet>
.in_base_set(CoreSet::PreUpdate)
.after(PickingSystem::UpdateIntersections),
);
}
Expand All @@ -228,9 +201,10 @@ impl Plugin for DebugCursorPickingPlugin {
pub struct DebugEventsPickingPlugin;
impl Plugin for DebugEventsPickingPlugin {
fn build(&self, app: &mut App) {
app.add_system_to_stage(
CoreStage::PreUpdate,
event_debug_system.after(PickingSystem::Events),
app.add_system(
event_debug_system
.in_base_set(CoreSet::PreUpdate)
.after(PickingSystem::Events),
);
}
}
Expand All @@ -250,7 +224,7 @@ impl Default for PickingCameraBundle {
}
}

#[derive(Bundle, Default)]
#[derive(Bundle)]
pub struct PickableBundle {
pub pickable_mesh: PickableMesh,
pub interaction: Interaction,
Expand All @@ -259,3 +233,16 @@ pub struct PickableBundle {
pub selection: Selection,
pub hover: Hover,
}

impl Default for PickableBundle {
fn default() -> Self {
Self {
pickable_mesh: default(),
interaction: default(),
focus_policy: FocusPolicy::Block,
highlight: default(),
selection: default(),
hover: default(),
}
}
}
Loading