Implement pause-aware picking

This changes the way 2D & 3D physics picking behaves in relation to pause:
- When pause is set, every collision object that is hovered or captured (3D only) is released from that condition, getting the relevant mouse-exit callback., unless its pause mode makes it immune from pause.
- During the pause. picking only considers collision objects immune from pause, sending input events and enter/exit callbacks to them as expected.
- When pause is left, nothing happens. This is a big difference with the classic behavior, which at this point would process all the input events that have been queued against the current state of the 2D/3D world (in other words, checking them against the current position of the objects instead of those at the time of the events).
This commit is contained in:
Pedro J. Estébanez 2021-02-06 21:14:35 +01:00
parent 89a43d9c2e
commit a63996ac95
3 changed files with 295 additions and 267 deletions

View File

@ -409,6 +409,7 @@ bool SceneTree::physics_process(float p_time) {
emit_signal("physics_frame");
_notify_group_pause("physics_process_internal", Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
call_group_flags(GROUP_CALL_REALTIME, "_viewports", "_process_picking");
_notify_group_pause("physics_process", Node::NOTIFICATION_PHYSICS_PROCESS);
_flush_ugc();
MessageQueue::get_singleton()->flush(); //small little hack

View File

@ -568,8 +568,38 @@ void Viewport::_notification(int p_what) {
RS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
}
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
_drop_physics_mouseover();
// Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not
// drop the gui mouseover here, as a scrollbar may be dragged while the
// mouse is outside the window (without the window having lost focus).
// See bug #39634
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_drop_physics_mouseover();
if (gui.mouse_focus && !gui.forced_mouse_focus) {
_drop_mouse_focus();
}
} break;
}
}
void Viewport::_process_picking() {
if (!is_inside_tree()) {
return;
}
if (!physics_object_picking) {
return;
}
if (to_screen_rect != Rect2i() && Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
return;
}
_drop_physics_mouseover(true);
if (physics_object_picking && (to_screen_rect == Rect2i() || Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED)) {
#ifndef _3D_DISABLED
Vector2 last_pos(1e20, 1e20);
CollisionObject3D *last_object = nullptr;
@ -700,7 +730,7 @@ void Viewport::_notification(int p_what) {
for (int i = 0; i < rc; i++) {
if (res[i].collider_id.is_valid() && res[i].collider) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
if (co) {
if (co && co->can_process()) {
bool send_event = true;
if (is_mouse) {
Map<ObjectID, uint64_t>::Element *F = physics_2d_mouseover.find(res[i].collider_id);
@ -788,7 +818,7 @@ void Viewport::_notification(int p_what) {
ObjectID new_collider;
if (col) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(result.collider);
if (co) {
if (co && co->can_process()) {
_collision_object_input_event(co, camera, ev, result.position, result.normal, result.shape);
last_object = co;
last_id = result.collider_id;
@ -823,25 +853,6 @@ void Viewport::_notification(int p_what) {
}
#endif
}
}
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
_drop_physics_mouseover();
// Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not
// drop the gui mouseover here, as a scrollbar may be dragged while the
// mouse is outside the window (without the window having lost focus).
// See bug #39634
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
_drop_physics_mouseover();
if (gui.mouse_focus && !gui.forced_mouse_focus) {
_drop_mouse_focus();
}
} break;
}
}
RID Viewport::get_viewport_rid() const {
@ -2582,28 +2593,41 @@ void Viewport::_drop_mouse_focus() {
}
}
void Viewport::_drop_physics_mouseover() {
void Viewport::_drop_physics_mouseover(bool p_paused_only) {
physics_has_last_mousepos = false;
while (physics_2d_mouseover.size()) {
Object *o = ObjectDB::get_instance(physics_2d_mouseover.front()->key());
List<Map<ObjectID, uint64_t>::Element *> to_erase;
for (Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.front(); E; E = E->next()) {
Object *o = ObjectDB::get_instance(E->key());
if (o) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
co->_mouse_exit();
if (co) {
if (p_paused_only && co->can_process()) {
continue;
}
physics_2d_mouseover.erase(physics_2d_mouseover.front());
co->_mouse_exit();
to_erase.push_back(E);
}
}
}
while (to_erase.size()) {
physics_2d_mouseover.erase(to_erase.front()->get());
to_erase.pop_front();
}
#ifndef _3D_DISABLED
if (physics_object_over.is_valid()) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
if (co) {
if (!(p_paused_only && co->can_process())) {
co->_mouse_exit();
}
}
physics_object_over = ObjectID();
physics_object_capture = ObjectID();
}
}
}
#endif
}
@ -3529,6 +3553,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lod_threshold", "pixels"), &Viewport::set_lod_threshold);
ClassDB::bind_method(D_METHOD("get_lod_threshold"), &Viewport::get_lod_threshold);
ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", 0), "set_world_2d", "get_world_2d");

View File

@ -450,7 +450,7 @@ private:
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
void _drop_mouse_focus();
void _drop_physics_mouseover();
void _drop_physics_mouseover(bool p_paused_only = false);
void _update_canvas_items(Node *p_node);
@ -479,6 +479,7 @@ protected:
bool _is_size_allocated() const;
void _notification(int p_what);
void _process_picking();
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;