mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
improved selection in the editor
- select hull-less objects using small-sized aabb - fixed incorrect position of intersection points when selecting a hull-less object
This commit is contained in:
+19
-14
@@ -18,6 +18,7 @@
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
use crate::settings::selection::SelectionSettings;
|
||||
use crate::{
|
||||
fyrox::{
|
||||
core::{
|
||||
@@ -121,7 +122,6 @@ struct PickContext {
|
||||
|
||||
pub type PickingFilter<'a> = Option<&'a mut dyn FnMut(Handle<Node>, &Node) -> bool>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PickingOptions<'a> {
|
||||
pub cursor_pos: Vector2<f32>,
|
||||
pub editor_only: bool,
|
||||
@@ -129,6 +129,7 @@ pub struct PickingOptions<'a> {
|
||||
pub ignore_back_faces: bool,
|
||||
pub use_picking_loop: bool,
|
||||
pub only_meshes: bool,
|
||||
pub settings: &'a SelectionSettings,
|
||||
}
|
||||
|
||||
impl CameraController {
|
||||
@@ -672,6 +673,7 @@ impl CameraController {
|
||||
ignore_back_faces,
|
||||
use_picking_loop,
|
||||
only_meshes,
|
||||
settings,
|
||||
} = options;
|
||||
|
||||
if let Some(camera) = graph[self.camera].cast::<Camera>() {
|
||||
@@ -726,7 +728,7 @@ impl CameraController {
|
||||
.transform(&node.global_transform())
|
||||
};
|
||||
// Do coarse, but fast, intersection test with bounding box first.
|
||||
if let Some(points) = ray.aabb_intersection_points(&aabb) {
|
||||
if ray.aabb_intersection_points(&aabb).is_some() {
|
||||
let result = precise_ray_test(node, camera, graph, &ray, ignore_back_faces);
|
||||
if result.has_hull() {
|
||||
if let Some(position) = result.pick_position {
|
||||
@@ -738,18 +740,21 @@ impl CameraController {
|
||||
}
|
||||
} else if !only_meshes {
|
||||
// Hull-less objects (light sources, cameras, etc.) can still be selected
|
||||
// by coarse intersection test results.
|
||||
let da = points[0].metric_distance(&ray.origin);
|
||||
let db = points[1].metric_distance(&ray.origin);
|
||||
let closest_distance = da.min(db);
|
||||
context.pick_list.push(CameraPickResult {
|
||||
position: transform_vertex(
|
||||
if da < db { points[0] } else { points[1] },
|
||||
&node.global_transform(),
|
||||
),
|
||||
node: handle,
|
||||
toi: closest_distance,
|
||||
});
|
||||
// by coarse intersection test with a simplified bounding box.
|
||||
let simple_aabb = AxisAlignedBoundingBox::from_radius(
|
||||
settings.hull_less_object_selection_radius,
|
||||
)
|
||||
.transform(&node.global_transform());
|
||||
if let Some(points) = ray.aabb_intersection_points(&simple_aabb) {
|
||||
let da = points[0].metric_distance(&ray.origin);
|
||||
let db = points[1].metric_distance(&ray.origin);
|
||||
let closest_distance = da.min(db);
|
||||
context.pick_list.push(CameraPickResult {
|
||||
position: if da < db { points[0] } else { points[1] },
|
||||
node: handle,
|
||||
toi: closest_distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +215,7 @@ impl MoveContext {
|
||||
// We need info only about closest intersection.
|
||||
use_picking_loop: false,
|
||||
only_meshes: true,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
Some(result.position)
|
||||
@@ -309,7 +310,7 @@ impl InteractionMode for MoveInteractionMode {
|
||||
engine: &mut Engine,
|
||||
mouse_pos: Vector2<f32>,
|
||||
frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -327,6 +328,7 @@ impl InteractionMode for MoveInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
if let Some(plane_kind) = self.move_gizmo.handle_pick(result.node, graph) {
|
||||
@@ -405,6 +407,7 @@ impl InteractionMode for MoveInteractionMode {
|
||||
ignore_back_faces: settings.selection.ignore_back_faces,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|result| {
|
||||
@@ -470,6 +473,7 @@ impl InteractionMode for MoveInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|r| r.node)
|
||||
|
||||
@@ -270,6 +270,7 @@ impl InteractionMode for EditNavmeshMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|r| r.node)
|
||||
@@ -414,7 +415,7 @@ impl InteractionMode for EditNavmeshMode {
|
||||
controller: &mut dyn SceneController,
|
||||
engine: &mut Engine,
|
||||
frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -435,6 +436,7 @@ impl InteractionMode for EditNavmeshMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|r| r.node)
|
||||
|
||||
@@ -79,7 +79,7 @@ impl InteractionMode for RotateInteractionMode {
|
||||
engine: &mut Engine,
|
||||
mouse_pos: Vector2<f32>,
|
||||
_frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -97,6 +97,7 @@ impl InteractionMode for RotateInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
if self.rotation_gizmo.handle_pick(result.node, graph) {
|
||||
@@ -163,6 +164,7 @@ impl InteractionMode for RotateInteractionMode {
|
||||
ignore_back_faces: settings.selection.ignore_back_faces,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|result| {
|
||||
@@ -248,6 +250,7 @@ impl InteractionMode for RotateInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|r| r.node)
|
||||
|
||||
@@ -78,7 +78,7 @@ impl InteractionMode for ScaleInteractionMode {
|
||||
engine: &mut Engine,
|
||||
mouse_pos: Vector2<f32>,
|
||||
_frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -97,6 +97,7 @@ impl InteractionMode for ScaleInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
if self.scale_gizmo.handle_pick(result.node, graph) {
|
||||
@@ -157,6 +158,7 @@ impl InteractionMode for ScaleInteractionMode {
|
||||
ignore_back_faces: settings.selection.ignore_back_faces,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|result| {
|
||||
@@ -192,7 +194,7 @@ impl InteractionMode for ScaleInteractionMode {
|
||||
controller: &mut dyn SceneController,
|
||||
engine: &mut Engine,
|
||||
frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -229,6 +231,7 @@ impl InteractionMode for ScaleInteractionMode {
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
)
|
||||
.map(|r| r.node)
|
||||
|
||||
@@ -376,7 +376,7 @@ impl InteractionMode for ColliderShapeInteractionMode {
|
||||
engine: &mut Engine,
|
||||
mouse_position: Vector2<f32>,
|
||||
_frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -390,7 +390,10 @@ impl InteractionMode for ColliderShapeInteractionMode {
|
||||
cursor_pos: mouse_position,
|
||||
editor_only: true,
|
||||
filter: Some(&mut |handle, _| handle != self.move_gizmo.origin),
|
||||
..Default::default()
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
let initial_position = scene.graph[result.node].global_position();
|
||||
@@ -507,7 +510,7 @@ impl InteractionMode for ColliderShapeInteractionMode {
|
||||
controller: &mut dyn SceneController,
|
||||
engine: &mut Engine,
|
||||
frame_size: Vector2<f32>,
|
||||
_settings: &Settings,
|
||||
settings: &Settings,
|
||||
) {
|
||||
let Some(game_scene) = controller.downcast_mut::<GameScene>() else {
|
||||
return;
|
||||
@@ -524,7 +527,10 @@ impl InteractionMode for ColliderShapeInteractionMode {
|
||||
cursor_pos: mouse_position,
|
||||
editor_only: true,
|
||||
filter: Some(&mut |handle, _| handle != self.move_gizmo.origin),
|
||||
..Default::default()
|
||||
ignore_back_faces: false,
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
if self.shape_gizmo.has_handle(result.node) {
|
||||
|
||||
@@ -635,6 +635,7 @@ impl SceneController for GameScene {
|
||||
// We need info only about closest intersection.
|
||||
use_picking_loop: false,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
Some(result.position)
|
||||
@@ -721,6 +722,7 @@ impl SceneController for GameScene {
|
||||
ignore_back_faces: settings.selection.ignore_back_faces,
|
||||
use_picking_loop: true,
|
||||
only_meshes: false,
|
||||
settings: &settings.selection,
|
||||
},
|
||||
) {
|
||||
let texture = tex.clone();
|
||||
|
||||
@@ -21,19 +21,27 @@
|
||||
use crate::fyrox::core::reflect::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Clone, Debug, Reflect, Eq)]
|
||||
#[derive(Deserialize, Serialize, PartialEq, Clone, Debug, Reflect)]
|
||||
pub struct SelectionSettings {
|
||||
pub ignore_back_faces: bool,
|
||||
|
||||
#[serde(default = "default_hull_less_object_selection_radius")]
|
||||
pub hull_less_object_selection_radius: f32,
|
||||
|
||||
// Hidden because there's a separate switch in world viewer for this.
|
||||
#[reflect(hidden)]
|
||||
pub track_selection: bool,
|
||||
}
|
||||
|
||||
fn default_hull_less_object_selection_radius() -> f32 {
|
||||
0.33
|
||||
}
|
||||
|
||||
impl Default for SelectionSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ignore_back_faces: false,
|
||||
hull_less_object_selection_radius: default_hull_less_object_selection_radius(),
|
||||
track_selection: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +742,7 @@ impl Base {
|
||||
/// a placeholder, because there is not information to calculate actual bounding box.
|
||||
#[inline]
|
||||
pub fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
|
||||
AxisAlignedBoundingBox::default()
|
||||
AxisAlignedBoundingBox::unit()
|
||||
}
|
||||
|
||||
/// Returns current **world-space** bounding box.
|
||||
|
||||
Reference in New Issue
Block a user