From 82902656ac6c60c40979e9cb513b65d96119f917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Tue, 5 Mar 2019 22:31:02 +0100 Subject: [PATCH] Improve/fix picking Acknowledge mouse button events as position tellers (to make picking more solid; for instance, the touch mouse is raised with a mouse unpressed event that may have a more current position) Forget mouse position for physics if touch mouse raised (because the position known as last is no longer meaningful) Remove needless check for mouse over/exit (now there's code to inject an spurious move for cases where camera/objects have moved) Restrict 2D mouse over/exit to mouse events (including emulated from touch; true touches shouldn't trigger the signals) Fixes #26460. --- main/input_default.cpp | 3 + scene/main/viewport.cpp | 139 ++++++++++++++++++---------------------- scene/main/viewport.h | 1 - 3 files changed, 65 insertions(+), 78 deletions(-) diff --git a/main/input_default.cpp b/main/input_default.cpp index e8133f9ebaa..65910b34bce 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -355,6 +355,7 @@ void InputDefault::_parse_input_event_impl(const Ref &p_event, bool Ref button_event; button_event.instance(); + button_event->set_device(-1); button_event->set_position(st->get_position()); button_event->set_global_position(st->get_position()); button_event->set_pressed(st->is_pressed()); @@ -383,6 +384,7 @@ void InputDefault::_parse_input_event_impl(const Ref &p_event, bool Ref motion_event; motion_event.instance(); + motion_event->set_device(-1); motion_event->set_position(sd->get_position()); motion_event->set_global_position(sd->get_position()); motion_event->set_relative(sd->get_relative()); @@ -600,6 +602,7 @@ void InputDefault::ensure_touch_mouse_raised() { Ref button_event; button_event.instance(); + button_event->set_device(-1); button_event->set_position(mouse_pos); button_event->set_global_position(mouse_pos); button_event->set_pressed(false); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index d86c241ec62..4f1330ee36e 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -251,31 +251,6 @@ void Viewport::_collision_object_input_event(CollisionObject *p_object, Camera * physics_last_id = id; } -void Viewport::_test_new_mouseover(ObjectID new_collider) { -#ifndef _3D_DISABLED - if (new_collider != physics_object_over) { - - if (physics_object_over) { - - CollisionObject *co = Object::cast_to(ObjectDB::get_instance(physics_object_over)); - if (co) { - co->_mouse_exit(); - } - } - - if (new_collider) { - - CollisionObject *co = Object::cast_to(ObjectDB::get_instance(new_collider)); - if (co) { - co->_mouse_enter(); - } - } - - physics_object_over = new_collider; - } -#endif -} - void Viewport::_notification(int p_what) { switch (p_what) { @@ -424,18 +399,19 @@ void Viewport::_notification(int p_what) { bool discard_empty_motion = false; - { // if no motion event exists, create a new one. This is necessary because objects or camera may have moved. + if (physics_has_last_mousepos) { + // if no mouse event exists, create a motion one. This is necessary because objects or camera may have moved. // while this extra event is sent, it is checked if both camera and last object and last ID did not move. If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame - bool has_mouse_motion = false; + bool has_mouse_event = false; for (List >::Element *E = physics_picking_events.front(); E; E = E->next()) { - Ref mm = E->get(); - if (mm.is_valid()) { - has_mouse_motion = true; + Ref m = E->get(); + if (m.is_valid()) { + has_mouse_event = true; break; } } - if (!has_mouse_motion && physics_has_last_mousepos) { + if (!has_mouse_event) { Ref mm; mm.instance(); mm->set_global_position(physics_last_mousepos); @@ -450,21 +426,21 @@ void Viewport::_notification(int p_what) { } } - bool motion_tested = false; - while (physics_picking_events.size()) { Ref ev = physics_picking_events.front()->get(); physics_picking_events.pop_front(); Vector2 pos; + bool is_mouse = false; Ref mm = ev; if (mm.is_valid()) { pos = mm->get_position(); - motion_tested = true; + is_mouse = true; + physics_has_last_mousepos = true; physics_last_mousepos = pos; physics_last_mouse_state.alt = mm->get_alt(); @@ -477,7 +453,12 @@ void Viewport::_notification(int p_what) { Ref mb = ev; if (mb.is_valid()) { + pos = mb->get_position(); + is_mouse = true; + + physics_has_last_mousepos = true; + physics_last_mousepos = pos; physics_last_mouse_state.alt = mb->get_alt(); physics_last_mouse_state.shift = mb->get_shift(); physics_last_mouse_state.control = mb->get_control(); @@ -487,6 +468,11 @@ void Viewport::_notification(int p_what) { physics_last_mouse_state.mouse_mask |= (1 << (mb->get_button_index() - 1)); } else { physics_last_mouse_state.mouse_mask &= ~(1 << (mb->get_button_index() - 1)); + + // If touch mouse raised, assume we don't know last mouse pos until new events come + if (mb->get_device() == -1) { + physics_has_last_mousepos = false; + } } } @@ -540,12 +526,14 @@ void Viewport::_notification(int p_what) { CollisionObject2D *co = Object::cast_to(res[i].collider); if (co) { - Map::Element *F = physics_2d_mouseover.find(res[i].collider_id); - if (!F) { - F = physics_2d_mouseover.insert(res[i].collider_id, frame); - co->_mouse_enter(); - } else { - F->get() = frame; + if (is_mouse) { + Map::Element *F = physics_2d_mouseover.find(res[i].collider_id); + if (!F) { + F = physics_2d_mouseover.insert(res[i].collider_id, frame); + co->_mouse_enter(); + } else { + F->get() = frame; + } } co->_input_event(this, ev, res[i].shape); @@ -554,25 +542,27 @@ void Viewport::_notification(int p_what) { } } - List::Element *> to_erase; + if (is_mouse) { + List::Element *> to_erase; - for (Map::Element *E = physics_2d_mouseover.front(); E; E = E->next()) { - if (E->get() != frame) { - Object *o = ObjectDB::get_instance(E->key()); - if (o) { + for (Map::Element *E = physics_2d_mouseover.front(); E; E = E->next()) { + if (E->get() != frame) { + Object *o = ObjectDB::get_instance(E->key()); + if (o) { - CollisionObject2D *co = Object::cast_to(o); - if (co) { - co->_mouse_exit(); + CollisionObject2D *co = Object::cast_to(o); + if (co) { + co->_mouse_exit(); + } } + to_erase.push_back(E); } - to_erase.push_back(E); } - } - while (to_erase.size()) { - physics_2d_mouseover.erase(to_erase.front()->get()); - to_erase.pop_front(); + while (to_erase.size()) { + physics_2d_mouseover.erase(to_erase.front()->get()); + to_erase.pop_front(); + } } } @@ -634,36 +624,31 @@ void Viewport::_notification(int p_what) { } } - if (mm.is_valid()) { - _test_new_mouseover(new_collider); + if (is_mouse && new_collider != physics_object_over) { + + if (physics_object_over) { + + CollisionObject *co = Object::cast_to(ObjectDB::get_instance(physics_object_over)); + if (co) { + co->_mouse_exit(); + } + } + + if (new_collider) { + + CollisionObject *co = Object::cast_to(ObjectDB::get_instance(new_collider)); + if (co) { + co->_mouse_enter(); + } + } + + physics_object_over = new_collider; } } last_pos = pos; } } - } - - if (!motion_tested && camera && physics_has_last_mousepos) { - - //test anyway for mouseenter/exit because objects might move - Vector3 from = camera->project_ray_origin(physics_last_mousepos); - Vector3 dir = camera->project_ray_normal(physics_last_mousepos); - - PhysicsDirectSpaceState *space = PhysicsServer::get_singleton()->space_get_direct_state(find_world()->get_space()); - if (space) { - - bool col = space->intersect_ray(from, from + dir * 10000, result, Set(), 0xFFFFFFFF, true, true, true); - ObjectID new_collider = 0; - if (col) { - CollisionObject *co = Object::cast_to(result.collider); - if (co) { - new_collider = result.collider_id; - } - } - - _test_new_mouseover(new_collider); - } #endif } } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index b8b5bf07a7c..28a52ac4b68 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -226,7 +226,6 @@ private: bool handle_input_locally; bool local_input_handled; - void _test_new_mouseover(ObjectID new_collider); Map physics_2d_mouseover; Ref world_2d;