Object picking (#8)

This PR implements basic object selection, with help from `bevy_mod_picking` and
`bevy_mod_outline`.

### Work Done
 * Click to select objects in the world
 * Selected objects become visually highlighted
 * The selected entity is available globally on the `Res<Selection>` resource
 * Selected entities can also be queried via the `Selected` marker component
 * Pressing `F` will "focus" the camera on the selected entity
 * Pressing `Esc` will clear the active selection

### Known Issues
 * We're currently using `bevy_mod_picking`'s "Selection" feature, which isn't 
   aware of our own input events. As a consequence, it will select the object
   under the cursor when releasing `LMB` after orbiting the camera, which is not
   what we want. This should be pretty easily fixable by dropping
   `bevy_mod_picking`'s selection feature and reading the object under the
   cursor in our own click listener that only runs if the LMB event is
   unhandled.
 * The skinned mesh is not currently selectable. This is likely fixable by
   adjusting the logic in `scene::mark_scene_objects`... somehow. The `Gltf`
   data structure is kind of a nightmare to work with, so this will take some
   investigation.

### Future Enhancements
 * For parity with other DCC apps, the "Focus" action should also adjust the
   camera zoom level to "frame" the selected object in the view. Punting on that
   for now because I'm not super confident about the math involved.
 * Group selection with Shift/Ctrl and/or marquee selection would be really nice
   to have, but probably not critical for now.
This commit is contained in:
Danny McGee
2023-11-02 13:32:58 -04:00
committed by GitHub
parent ce6db7fc3c
commit 06751e54f6
30 changed files with 3405 additions and 48 deletions
+5 -3
View File
@@ -38,9 +38,9 @@ where
f32: Lerp<T>,
T: Copy,
{
fn tick(&mut self, dt: f32) -> T {
fn tick(&mut self, dt: f32) -> &mut Self {
self.t = (self.t + dt).clamp(0., 1.);
self.current()
self
}
pub fn current(&self) -> T {
@@ -52,6 +52,8 @@ where
let t = match self.easing {
Easing::Linear => self.t,
Easing::Sine => self.t.curve_sin(),
Easing::SineIn => self.t.curve_sin_in(),
Easing::SineOut => self.t.curve_sin_out(),
};
t.lerp(self.initial, self.target)
@@ -97,7 +99,7 @@ fn interp_velocity(time: Res<Time>, mut q_interp: Query<(&mut Velocity, &mut Int
for (mut velocity, mut interp) in q_interp.iter_mut() {
let dt = delta_seconds / interp.accel_zero2max;
**velocity = interp.tick(dt);
**velocity = interp.tick(dt).current();
}
}
+3
View File
@@ -10,6 +10,9 @@ mod traits;
pub enum Easing {
Linear,
Sine,
SineOut,
#[allow(dead_code)]
SineIn,
}
impl Default for Easing {
+36
View File
@@ -73,6 +73,18 @@ pub trait CurveSin {
/// }
/// ```
fn curve_sin(self) -> Self;
/// Works like `curve_sin`, except that only the lower half of the line is
/// curved, with the upper half progressing linearly.
///
/// https://www.desmos.com/calculator/ngceukl6g5
fn curve_sin_in(self) -> Self;
/// Works like `curve_sin`, except that only the upper half of the line is
/// curved, with the lower half progressing linearly.
///
/// https://www.desmos.com/calculator/umvvxhkd46
fn curve_sin_out(self) -> Self;
}
impl<T, Operand> Lerp<Operand> for T
@@ -141,6 +153,18 @@ impl CurveSin for f32 {
-1. * ((self * PI).cos() / 2.) + 0.5
}
fn curve_sin_in(self) -> Self {
use std::f32::consts::PI;
1. - ((self * PI) / 2.).cos()
}
fn curve_sin_out(self) -> Self {
use std::f32::consts::PI;
((self * PI) / 2.).sin()
}
}
impl CurveSin for f64 {
@@ -149,4 +173,16 @@ impl CurveSin for f64 {
-1. * ((self * PI).cos() / 2.) + 0.5
}
fn curve_sin_in(self) -> Self {
use std::f64::consts::PI;
1. - ((self * PI) / 2.).cos()
}
fn curve_sin_out(self) -> Self {
use std::f64::consts::PI;
((self * PI) / 2.).sin()
}
}