break down select and pointer hover

This allows us to hook into the hovered piece for debugging
main
Elijah C. Voigt 2 years ago
parent 63ca981aa4
commit 4cebce8bf2

@ -13,6 +13,7 @@ impl Plugin for Display3dPlugin {
MaterialPlugin::<DissolveMaterial>::default(),
))
.init_state::<DissolvingAnimation>()
.init_resource::<PiecePointer>()
.insert_resource(Msaa::Off)
.insert_resource(AmbientLight {
color: Color::WHITE,
@ -69,6 +70,16 @@ impl Plugin for Display3dPlugin {
set_title_model.run_if(any_component_added::<TitleText>()),
set_valid_move_model.run_if(any_component_added::<game::ValidMove>()),
set_tile_hitbox.run_if(any_component_added::<game::Tile>()),
entity_pointer
.run_if(in_state(GameState::Play))
.run_if(in_state(DisplayState::Display3d))
// Run if in debug state on mouse events
// Or just run when the left mouse button is clicked
.run_if(
in_state(debug::DebugState::Enabled)
.and_then(on_event::<MouseMotion>())
.or_else(just_pressed(MouseButton::Left))
),
select
.run_if(in_state(GameState::Play))
.run_if(in_state(DisplayState::Display3d))
@ -653,93 +664,96 @@ fn update_pieces(
);
}
fn select(
// Query Selectable with BoardIndex
query: Query<(&BoardIndex, &Side), With<Selectable>>,
piece: Res<PiecePointer>,
mut selections: EventWriter<game::Selection>,
selected: Query<Entity, With<game::Selected>>,
state: Res<State<game::TurnState>>,
) {
// info!("Board index selected: {:?}", board_index);
match *piece {
PiecePointer(Some(e)) => {
query.get(e).iter().for_each(|(board_index, side)| {
let side_check = !selected.is_empty() || state.get().0 == **side;
if side_check {
selections.send(game::Selection(**board_index));
}
});
},
_ => ()
}
}
#[derive(Debug, Default, Resource)]
struct PiecePointer(Option<Entity>);
/// Select tiles and pieces in 3d
/// There is a bug where we are selecting multiple entities...
/// TODO: Selectable generalize picking pieces **and** hitboxes
fn select(
mut events: EventReader<MouseButtonInput>,
fn entity_pointer(
query: Query<(Entity, &Handle<Mesh>, &GlobalTransform)>,
meshes: Res<Assets<Mesh>>,
cameras: Query<(&Camera, &GlobalTransform)>,
windows: Query<&Window, With<PrimaryWindow>>,
selectable: Query<(Entity, &BoardIndex, &Side), (With<game::Selectable>, With<Display3d>)>,
selected: Query<Entity, With<game::Selected>>,
selectable: Query<Entity, (With<game::Selectable>, With<Display3d>)>,
children: Query<&Children>,
mut selections: EventWriter<game::Selection>,
state: Res<State<game::TurnState>>,
mut pointer: ResMut<PiecePointer>,
) {
// For every mouse click event
events
.read()
// Only read button presses
.filter(|ev| ev.state == ButtonState::Pressed && ev.button == MouseButton::Left)
.for_each(|_| {
// For each window (there should be only one)
windows.iter().for_each(|window| {
// Get the cursor position
if let Some(pos) = window.cursor_position() {
// iterate over every camera
cameras.iter().for_each(|(camera, gt)| {
// Get a ray from the camera through the cursor into the world
if let Some(ray) = camera.viewport_to_world(gt, pos) {
// Iterate over every entity with a 3d scene
query
// For each window (there should be only one)
windows.iter().for_each(|window| {
// Get the cursor position
if let Some(pos) = window.cursor_position() {
// iterate over every camera
cameras.iter().for_each(|(camera, gt)| {
// Get a ray from the camera through the cursor into the world
if let Some(ray) = camera.viewport_to_world(gt, pos) {
// Iterate over every entity with a 3d scene
let selected = query
.iter()
// Transform the scene handle into mesh data
.filter_map(|(entity, handle, gt)| {
meshes.get(handle).map(|mesh| (entity, mesh, gt))
})
// Iterate over every mesh + global transform
.filter_map(|(entity, mesh, gt)| {
// If the camera -> cursor -> * ray intersects with the mesh
hit3d::intersects3d(&ray, mesh, gt).map(|hit| (entity, hit))
})
.filter_map(|(entity, hit)| {
// Find this entity in the set of selectable entities
selectable
.iter()
// Transform the scene handle into mesh data
.filter_map(|(entity, handle, gt)| {
meshes.get(handle).map(|mesh| (entity, mesh, gt))
})
// Iterate over every mesh + global transform
.filter_map(|(entity, mesh, gt)| {
// If the camera -> cursor -> * ray intersects with the mesh
hit3d::intersects3d(&ray, mesh, gt).map(|hit| (entity, hit))
})
.filter_map(|(entity, hit)| {
// Find this entity in the set of selectable entities
selectable
.iter()
.find_map(|(e, &board_index, &side)| {
// Check the side of the selection if no piece is selected
// Otherwise this is fine, select away
let side_check =
!selected.is_empty() || state.get().0 == side;
let hit_check = {
// This entity was hit (tile hitboxes)
let primary = entity == e;
// A child was hit (pieces)
let secondary = children
.iter_descendants(e)
.any(|child| child == entity);
primary || secondary
};
// Return the board index of this piece
(side_check && hit_check).then_some(board_index)
})
.map(|board_index| {
// Include the hit from the camera
(hit, board_index)
})
})
// Compare the distance of all hits, choosing the closest one
.min_by(|(hit_a, _), (hit_b, _)| {
hit_a.distance.partial_cmp(&hit_b.distance).unwrap()
.find_map(|e| {
let hit_check = {
// This entity was hit (tile hitboxes)
let primary = entity == e;
// A child was hit (pieces)
let secondary = children
.iter_descendants(e)
.any(|child| child == entity);
primary || secondary
};
// Return the board index of this piece
hit_check.then_some((e, hit.clone()))
})
// Iterate over the 0 or 1 outcomes of the above
.iter()
// Send an event that this board index was selected
.for_each(|(_, board_index)| {
info!("Board index selected: {:?}", board_index);
selections.send(game::Selection(*board_index));
});
}
});
})
// Compare the distance of all hits, choosing the closest one
.min_by(|(_, hit_a), (_, hit_b)| {
hit_a.distance.partial_cmp(&hit_b.distance).unwrap()
})
.map(|(e, _)| e);
*pointer = PiecePointer(selected)
}
});
});
}
});
}
fn selected_gizmo(

@ -1,10 +1,9 @@
use crate::prelude::*;
/// Hit data for 3d objects in Global (not Local) space and the distance from the Camera
#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct Hit3d {
pub distance: f32,
_point: Vec3,
}
/// A 3D Triangle used for ray-intersection tests
@ -102,7 +101,6 @@ pub(crate) fn intersects3d(ray: &Ray3d, mesh: &Mesh, gt: &GlobalTransform) -> Op
};
hit.then_some(Hit3d {
distance: d,
_point: p,
})
} else {
None

Loading…
Cancel
Save