@ -418,288 +418,302 @@ void Viewport::_notification(int p_what) {
VS : : get_singleton ( ) - > multimesh_set_visible_instances ( contact_3d_debug_multimesh , point_count ) ;
}
if ( physics_object_picking & & ( to_screen_rect = = Rect2 ( ) | | Input : : get_singleton ( ) - > get_mouse_mode ( ) ! = Input : : MOUSE_MODE_CAPTURED ) ) {
if ( ! GLOBAL_GET ( " physics/common/enable_pause_aware_picking " ) ) {
_process_picking ( false ) ;
}
} break ;
case SceneTree : : NOTIFICATION_WM_MOUSE_EXIT :
case SceneTree : : NOTIFICATION_WM_FOCUS_OUT : {
# ifndef _3D_DISABLED
Vector2 last_pos ( 1e20 , 1e20 ) ;
CollisionObject * last_object = NULL ;
ObjectID last_id = 0 ;
# endif
PhysicsDirectSpaceState : : RayResult result ;
Physics2DDirectSpaceState * ss2d = Physics2DServer : : get_singleton ( ) - > space_get_direct_state ( find_world_2d ( ) - > get_space ( ) ) ;
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_event = false ;
for ( List < Ref < InputEvent > > : : Element * E = physics_picking_events . front ( ) ; E ; E = E - > next ( ) ) {
Ref < InputEventMouse > m = E - > get ( ) ;
if ( m . is_valid ( ) ) {
has_mouse_event = true ;
break ;
}
}
_drop_physics_mouseover ( ) ;
if ( ! has_mouse_event ) {
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_device ( InputEvent : : DEVICE_ID_INTERNAL ) ;
mm - > set_global_position ( physics_last_mousepos ) ;
mm - > set_position ( physics_last_mousepos ) ;
mm - > set_alt ( physics_last_mouse_state . alt ) ;
mm - > set_shift ( physics_last_mouse_state . shift ) ;
mm - > set_control ( physics_last_mouse_state . control ) ;
mm - > set_metakey ( physics_last_mouse_state . meta ) ;
mm - > set_button_mask ( physics_last_mouse_state . mouse_mask ) ;
physics_picking_events . push_back ( mm ) ;
}
}
if ( gui . mouse_focus ) {
//if mouse is being pressed, send a release event
_drop_mouse_focus ( ) ;
}
} break ;
}
}
void Viewport : : _process_picking ( bool p_ignore_paused ) {
if ( ! is_inside_tree ( ) )
return ;
if ( ! physics_object_picking )
return ;
if ( to_screen_rect ! = Rect2 ( ) & & Input : : get_singleton ( ) - > get_mouse_mode ( ) = = Input : : MOUSE_MODE_CAPTURED )
return ;
while ( physics_picking_events . size ( ) ) {
if ( p_ignore_paused ) {
_drop_physics_mouseover ( true ) ;
}
Ref < InputEvent > ev = physics_picking_events . front ( ) - > get ( ) ;
physics_picking_events . pop_front ( ) ;
# ifndef _3D_DISABLED
Vector2 last_pos ( 1e20 , 1e20 ) ;
CollisionObject * last_object = NULL ;
ObjectID last_id = 0 ;
# endif
PhysicsDirectSpaceState : : RayResult result ;
Physics2DDirectSpaceState * ss2d = Physics2DServer : : get_singleton ( ) - > space_get_direct_state ( find_world_2d ( ) - > get_space ( ) ) ;
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_event = false ;
for ( List < Ref < InputEvent > > : : Element * E = physics_picking_events . front ( ) ; E ; E = E - > next ( ) ) {
Ref < InputEventMouse > m = E - > get ( ) ;
if ( m . is_valid ( ) ) {
has_mouse_event = true ;
break ;
}
}
Vector2 pos ;
bool is_mouse = false ;
if ( ! has_mouse_event ) {
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_device ( InputEvent : : DEVICE_ID_INTERNAL ) ;
mm - > set_global_position ( physics_last_mousepos ) ;
mm - > set_position ( physics_last_mousepos ) ;
mm - > set_alt ( physics_last_mouse_state . alt ) ;
mm - > set_shift ( physics_last_mouse_state . shift ) ;
mm - > set_control ( physics_last_mouse_state . control ) ;
mm - > set_metakey ( physics_last_mouse_state . meta ) ;
mm - > set_button_mask ( physics_last_mouse_state . mouse_mask ) ;
physics_picking_events . push_back ( mm ) ;
}
}
Ref < InputEventMouseMotion > mm = ev ;
while ( physics_picking_events . size ( ) ) {
if ( mm . is_valid ( ) ) {
Ref < InputEvent > ev = physics_picking_events . front ( ) - > get ( ) ;
physics_picking_events . pop_front ( ) ;
pos = mm - > get_position ( ) ;
is_mouse = true ;
Vector2 pos ;
bool is_mouse = fals e;
physics_has_last_mousepos = true ;
physics_last_mousepos = pos ;
physics_last_mouse_state . alt = mm - > get_alt ( ) ;
physics_last_mouse_state . shift = mm - > get_shift ( ) ;
physics_last_mouse_state . control = mm - > get_control ( ) ;
physics_last_mouse_state . meta = mm - > get_metakey ( ) ;
physics_last_mouse_state . mouse_mask = mm - > get_button_mask ( ) ;
}
Ref < InputEventMouseMotion > mm = ev ;
Ref < InputEventMouseButton > mb = ev ;
if ( mm . is_valid ( ) ) {
if ( mb . is_valid ( ) ) {
pos = mm - > get_position ( ) ;
is_mouse = true ;
pos = mb - > get_position ( ) ;
is_mouse = true ;
physics_has_last_mousepos = true ;
physics_last_mousepos = pos ;
physics_last_mouse_state . alt = mm - > get_alt ( ) ;
physics_last_mouse_state . shift = mm - > get_shift ( ) ;
physics_last_mouse_state . control = mm - > get_control ( ) ;
physics_last_mouse_state . meta = mm - > get_metakey ( ) ;
physics_last_mouse_state . mouse_mask = mm - > get_button_mask ( ) ;
}
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 ( ) ;
physics_last_mouse_state . meta = mb - > get_metakey ( ) ;
Ref < InputEventMouseButton > mb = ev ;
if ( mb - > is_pressed ( ) ) {
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 ( mb . is_valid ( ) ) {
// If touch mouse raised, assume we don't know last mouse pos until new events come
if ( mb - > get_device ( ) = = InputEvent : : DEVICE_ID_TOUCH_MOUSE ) {
physics_has_last_mousepos = false ;
}
}
}
pos = mb - > get_position ( ) ;
is_mouse = true ;
Ref < InputEventKey > k = ev ;
if ( k . is_valid ( ) ) {
//only for mask
physics_last_mouse_state . alt = k - > get_alt ( ) ;
physics_last_mouse_state . shift = k - > get_shift ( ) ;
physics_last_mouse_state . control = k - > get_control ( ) ;
physics_last_mouse_state . meta = k - > get_metakey ( ) ;
continue ;
}
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 ( ) ;
physics_last_mouse_state . meta = mb - > get_metakey ( ) ;
Ref < InputEventScreenDrag > sd = ev ;
if ( mb - > is_pressed ( ) ) {
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 ( sd . is_valid ( ) ) {
pos = sd - > get_position ( ) ;
}
// If touch mouse raised, assume we don't know last mouse pos until new events come
if ( mb - > get_device ( ) = = InputEvent : : DEVICE_ID_TOUCH_MOUSE ) {
physics_has_last_mousepos = false ;
}
}
}
Ref < InputEventScreenTouch > st = ev ;
Ref < InputEventKey > k = ev ;
if ( k . is_valid ( ) ) {
//only for mask
physics_last_mouse_state . alt = k - > get_alt ( ) ;
physics_last_mouse_state . shift = k - > get_shift ( ) ;
physics_last_mouse_state . control = k - > get_control ( ) ;
physics_last_mouse_state . meta = k - > get_metakey ( ) ;
continue ;
}
if ( st . is_valid ( ) ) {
pos = st - > get_position ( ) ;
}
Ref < InputEventScreenDrag > sd = ev ;
if ( ss2d ) {
//send to 2D
if ( sd . is_valid ( ) ) {
pos = sd - > get_position ( ) ;
}
uint64_t frame = get_tree ( ) - > get_frame ( ) ;
Ref < InputEventScreenTouch > st = ev ;
Physics2DDirectSpaceState : : ShapeResult res [ 64 ] ;
for ( Set < CanvasLayer * > : : Element * E = canvas_layers . front ( ) ; E ; E = E - > next ( ) ) {
Transform2D canvas_transform ;
ObjectID canvas_layer_id ;
if ( E - > get ( ) ) {
// A descendant CanvasLayer
canvas_transform = E - > get ( ) - > get_transform ( ) ;
canvas_layer_id = E - > get ( ) - > get_instance_id ( ) ;
} else {
// This Viewport's builtin canvas
canvas_transform = get_canvas_transform ( ) ;
canvas_layer_id = 0 ;
}
if ( st . is_valid ( ) ) {
pos = st - > get_position ( ) ;
}
Vector2 point = canvas_transform . affine_inverse ( ) . xform ( pos ) ;
int rc = ss2d - > intersect_point_on_canvas ( point , canvas_layer_id , res , 64 , Set < RID > ( ) , 0xFFFFFFFF , true , true , true ) ;
for ( int i = 0 ; i < rc ; i + + ) {
if ( res [ i ] . collider_id & & res [ i ] . collider ) {
CollisionObject2D * co = Object : : cast_to < CollisionObject2D > ( res [ i ] . collider ) ;
if ( co ) {
bool send_event = true ;
if ( is_mouse ) {
Map < ObjectID , uint64_t > : : Element * F = physics_2d_mouseover . find ( res [ i ] . collider_id ) ;
if ( ! F ) {
physics_2d_mouseover . insert ( res [ i ] . collider_id , frame ) ;
co - > _mouse_enter ( ) ;
} else {
F - > get ( ) = frame ;
// It was already hovered, so don't send the event if it's faked
if ( mm . is_valid ( ) & & mm - > get_device ( ) = = InputEvent : : DEVICE_ID_INTERNAL ) {
send_event = false ;
}
}
}
if ( send_event ) {
co - > _input_event ( this , ev , res [ i ] . shape ) ;
}
}
}
}
}
if ( ss2d ) {
//send to 2D
if ( is_mouse ) {
List < Map < ObjectID , uint64_t > : : Element * > to_erase ;
uint64_t frame = get_tree ( ) - > get_frame ( ) ;
for ( Map < ObjectID , uint64_t > : : Element * E = physics_2d_mouseover . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) ! = frame ) {
Object * o = ObjectDB : : get_instance ( E - > key ( ) ) ;
if ( o ) {
Physics2DDirectSpaceState : : ShapeResult res [ 64 ] ;
for ( Set < CanvasLayer * > : : Element * E = canvas_layers . front ( ) ; E ; E = E - > next ( ) ) {
Transform2D canvas_transform ;
ObjectID canvas_layer_id ;
if ( E - > get ( ) ) {
// A descendant CanvasLayer
canvas_transform = E - > get ( ) - > get_transform ( ) ;
canvas_layer_id = E - > get ( ) - > get_instance_id ( ) ;
} else {
// This Viewport's builtin canvas
canvas_transform = get_canvas_transform ( ) ;
canvas_layer_id = 0 ;
}
CollisionObject2D * co = Object : : cast_to < CollisionObject2D > ( o ) ;
if ( co ) {
co - > _mouse_exit ( ) ;
}
Vector2 point = canvas_transform . affine_inverse ( ) . xform ( pos ) ;
int rc = ss2d - > intersect_point_on_canvas ( point , canvas_layer_id , res , 64 , Set < RID > ( ) , 0xFFFFFFFF , true , true , true ) ;
for ( int i = 0 ; i < rc ; i + + ) {
if ( res [ i ] . collider_id & & res [ i ] . collider ) {
CollisionObject2D * co = Object : : cast_to < CollisionObject2D > ( res [ i ] . collider ) ;
if ( co & & ( ! p_ignore_paused | | co - > can_process ( ) ) ) {
bool send_event = true ;
if ( is_mouse ) {
Map < ObjectID , uint64_t > : : Element * F = physics_2d_mouseover . find ( res [ i ] . collider_id ) ;
if ( ! F ) {
physics_2d_mouseover . insert ( res [ i ] . collider_id , frame ) ;
co - > _mouse_enter ( ) ;
} else {
F - > get ( ) = frame ;
// It was already hovered, so don't send the event if it's faked
if ( mm . is_valid ( ) & & mm - > get_device ( ) = = InputEvent : : DEVICE_ID_INTERNAL ) {
send_event = false ;
}
to_erase . push_back ( E ) ;
}
}
while ( to_erase . size ( ) ) {
physics_2d_mouseover . erase ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
if ( send_event ) {
co - > _input_event ( this , ev , res [ i ] . shape ) ;
}
}
}
}
}
# ifndef _3D_DISABLED
bool captured = false ;
if ( is_mouse ) {
List < Map < ObjectID , uint64_t > : : Element * > to_era se;
if ( physics_object_capture ! = 0 ) {
for ( Map < ObjectID , uint64_t > : : Element * E = physics_2d_mouseover . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) ! = frame ) {
Object * o = ObjectDB : : get_instance ( E - > key ( ) ) ;
if ( o ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( physics_object_capture ) ) ;
if ( co & & camera ) {
_collision_object_input_event ( co , camera , ev , Vector3 ( ) , Vector3 ( ) , 0 ) ;
captured = true ;
if ( mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & ! mb - > is_pressed ( ) ) {
physics_object_capture = 0 ;
CollisionObject2D * co = Object : : cast_to < CollisionObject2D > ( o ) ;
if ( co ) {
co - > _mouse_exit ( ) ;
}
} else {
physics_object_capture = 0 ;
}
to_erase . push_back ( E ) ;
}
}
if ( captured ) {
//none
} else if ( pos = = last_pos ) {
while ( to_erase . size ( ) ) {
physics_2d_mouseover . erase ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
}
}
}
if ( last_id ) {
if ( ObjectDB : : get_instance ( last_id ) & & last_object ) {
//good, exists
_collision_object_input_event ( last_object , camera , ev , result . position , result . normal , result . shape ) ;
if ( last_object - > get_capture_input_on_drag ( ) & & mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & mb - > is_pressed ( ) ) {
physics_object_capture = last_id ;
}
}
}
} else {
# ifndef _3D_DISABLED
bool captured = false ;
if ( camera ) {
if ( physics_object_capture ! = 0 ) {
Vector3 from = camera - > project_ray_origin ( pos ) ;
Vector3 dir = camera - > project_ray_normal ( pos ) ;
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( physics_object_capture ) ) ;
if ( co & & camera ) {
_collision_object_input_event ( co , camera , ev , Vector3 ( ) , Vector3 ( ) , 0 ) ;
captured = true ;
if ( mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & ! mb - > is_pressed ( ) ) {
physics_object_capture = 0 ;
}
PhysicsDirectSpaceState * space = PhysicsServer : : get_singleton ( ) - > space_get_direct_state ( find_world ( ) - > get_space ( ) ) ;
if ( space ) {
} else {
physics_object_capture = 0 ;
}
}
bool col = space - > intersect_ray ( from , from + dir * 10000 , result , Set < RID > ( ) , 0xFFFFFFFF , true , true , true ) ;
ObjectID new_collider = 0 ;
if ( col ) {
if ( captured ) {
//none
} else if ( pos = = last_pos ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( result . collider ) ;
if ( co ) {
if ( last_id ) {
if ( ObjectDB : : get_instance ( last_id ) & & last_object ) {
//good, exists
_collision_object_input_event ( last_object , camera , ev , result . position , result . normal , result . shape ) ;
if ( last_object - > get_capture_input_on_drag ( ) & & mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & mb - > is_pressed ( ) ) {
physics_object_capture = last_id ;
}
}
}
} else {
_collision_object_input_event ( co , camera , ev , result . position , result . normal , result . shape ) ;
last_object = co ;
last_id = result . collider_id ;
new_collider = last_id ;
if ( co - > get_capture_input_on_drag ( ) & & mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & mb - > is_pressed ( ) ) {
physics_object_capture = last_id ;
}
}
}
if ( camera ) {
if ( is_mouse & & new_collider ! = physics_object_over ) {
Vector3 from = camera - > project_ray_origin ( pos ) ;
Vector3 dir = camera - > project_ray_normal ( pos ) ;
if ( physics_object_over ) {
PhysicsDirectSpaceState * space = PhysicsServer : : get_singleton ( ) - > space_get_direct_state ( find_world ( ) - > get_space ( ) ) ;
if ( space ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( physics_object_over ) ) ;
if ( co ) {
co - > _mouse_exit ( ) ;
}
}
bool col = space - > intersect_ray ( from , from + dir * 10000 , result , Set < RID > ( ) , 0xFFFFFFFF , true , true , true ) ;
ObjectID new_collider = 0 ;
if ( col ) {
if ( new_collider ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( result . collider ) ;
if ( co & & ( ! p_ignore_paused | | co - > can_process ( ) ) ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( new_collider ) ) ;
if ( co ) {
co - > _mouse_enter ( ) ;
}
}
_collision_object_input_event ( co , camera , ev , result . position , result . normal , result . shape ) ;
last_object = co ;
last_id = result . collider_id ;
new_collider = last_id ;
if ( co - > get_capture_input_on_drag ( ) & & mb . is_valid ( ) & & mb - > get_button_index ( ) = = 1 & & mb - > is_pressed ( ) ) {
physics_object_capture = last_id ;
}
}
}
physics_object_over = new_collider ;
}
if ( is_mouse & & new_collider ! = physics_object_over ) {
if ( physics_object_over ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( physics_object_over ) ) ;
if ( co ) {
co - > _mouse_exit ( ) ;
}
}
last_pos = pos ;
if ( new_collider ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( new_collider ) ) ;
if ( co ) {
co - > _mouse_enter ( ) ;
}
}
physics_object_over = new_collider ;
}
# endif
}
}
} break ;
case SceneTree : : NOTIFICATION_WM_MOUSE_EXIT :
case SceneTree : : NOTIFICATION_WM_FOCUS_OUT : {
_drop_physics_mouseover ( ) ;
if ( gui . mouse_focus ) {
//if mouse is being pressed, send a release event
_drop_mouse_focus ( ) ;
last_pos = pos ;
}
} break ;
}
# endif
}
}
@ -2735,28 +2749,42 @@ 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 ;
}
co - > _mouse_exit ( ) ;
to_erase . push_back ( E ) ;
}
}
physics_2d_mouseover . erase ( physics_2d_mouseover . front ( ) ) ;
}
while ( to_erase . size ( ) ) {
physics_2d_mouseover . erase ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
}
# ifndef _3D_DISABLED
if ( physics_object_over ) {
CollisionObject * co = Object : : cast_to < CollisionObject > ( ObjectDB : : get_instance ( physics_object_over ) ) ;
if ( co ) {
co - > _mouse_exit ( ) ;
if ( ! ( p_paused_only & & co - > can_process ( ) ) ) {
co - > _mouse_exit ( ) ;
physics_object_over = physics_object_capture = 0 ;
}
}
}
physics_object_over = physics_object_capture = 0 ;
# endif
}
@ -3297,6 +3325,8 @@ void Viewport::_bind_methods() {
ClassDB : : bind_method ( D_METHOD ( " _own_world_changed " ) , & Viewport : : _own_world_changed ) ;
ClassDB : : bind_method ( D_METHOD ( " _process_picking " , " ignore_paused " ) , & Viewport : : _process_picking ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " arvr " ) , " set_use_arvr " , " use_arvr " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : VECTOR2 , " size " ) , " set_size " , " get_size " ) ;