diff --git a/src/display3d.rs b/src/display3d.rs index 873c72b..898bac9 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -13,6 +13,7 @@ impl Plugin for Display3dPlugin { MaterialPlugin::::default(), )) .init_state::() + .init_resource::() .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::()), set_valid_move_model.run_if(any_component_added::()), set_tile_hitbox.run_if(any_component_added::()), + 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::()) + .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>, + piece: Res, + mut selections: EventWriter, + selected: Query>, + state: Res>, +) { + // 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); + /// 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, +fn entity_pointer( query: Query<(Entity, &Handle, &GlobalTransform)>, meshes: Res>, cameras: Query<(&Camera, &GlobalTransform)>, windows: Query<&Window, With>, - selectable: Query<(Entity, &BoardIndex, &Side), (With, With)>, - selected: Query>, + selectable: Query, With)>, children: Query<&Children>, - mut selections: EventWriter, - state: Res>, + mut pointer: ResMut, ) { - // 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( diff --git a/src/hit3d.rs b/src/hit3d.rs index a5a5481..70bc991 100644 --- a/src/hit3d.rs +++ b/src/hit3d.rs @@ -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