2017-04-23 22:10:41 +10:00
/*************************************************************************/
2020-04-09 00:47:36 +10:00
/* xr_nodes.cpp */
2017-04-23 22:10:41 +10:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2017-04-23 22:10:41 +10:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-04-23 22:10:41 +10:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2020-04-09 00:47:36 +10:00
# include "xr_nodes.h"
2020-05-19 11:24:58 +02:00
2022-02-12 02:46:22 +01:00
# include "core/config/project_settings.h"
2021-08-12 18:05:59 -05:00
# include "scene/main/viewport.h"
2020-04-09 00:47:36 +10:00
# include "servers/xr/xr_interface.h"
2017-04-23 22:10:41 +10:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-04-09 00:47:36 +10:00
void XRCamera3D : : _notification ( int p_what ) {
2017-04-23 22:10:41 +10:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
2020-04-09 00:47:36 +10:00
// need to find our XROrigin3D parent and let it know we're its camera!
XROrigin3D * origin = Object : : cast_to < XROrigin3D > ( get_parent ( ) ) ;
2020-04-02 01:20:12 +02:00
if ( origin ! = nullptr ) {
2017-04-23 22:10:41 +10:00
origin - > set_tracked_camera ( this ) ;
}
2022-02-15 18:06:48 +01:00
} break ;
2017-04-23 22:10:41 +10:00
case NOTIFICATION_EXIT_TREE : {
2020-04-09 00:47:36 +10:00
// need to find our XROrigin3D parent and let it know we're no longer its camera!
XROrigin3D * origin = Object : : cast_to < XROrigin3D > ( get_parent ( ) ) ;
2021-08-29 16:05:11 +10:00
if ( origin ! = nullptr & & origin - > get_tracked_camera ( ) = = this ) {
origin - > set_tracked_camera ( nullptr ) ;
2017-04-23 22:10:41 +10:00
}
2022-02-15 18:06:48 +01:00
} break ;
}
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
void XRCamera3D : : _changed_tracker ( const StringName p_tracker_name , int p_tracker_type ) {
if ( p_tracker_name = = tracker_name ) {
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
tracker = xr_server - > get_tracker ( p_tracker_name ) ;
if ( tracker . is_valid ( ) ) {
tracker - > connect ( " pose_changed " , callable_mp ( this , & XRCamera3D : : _pose_changed ) ) ;
Ref < XRPose > pose = tracker - > get_pose ( pose_name ) ;
if ( pose . is_valid ( ) ) {
set_transform ( pose - > get_adjusted_transform ( ) ) ;
}
}
}
}
void XRCamera3D : : _removed_tracker ( const StringName p_tracker_name , int p_tracker_type ) {
if ( p_tracker_name = = tracker_name ) {
if ( tracker . is_valid ( ) ) {
tracker - > disconnect ( " pose_changed " , callable_mp ( this , & XRCamera3D : : _pose_changed ) ) ;
}
tracker . unref ( ) ;
}
}
void XRCamera3D : : _pose_changed ( const Ref < XRPose > & p_pose ) {
if ( p_pose - > get_name ( ) = = pose_name ) {
set_transform ( p_pose - > get_adjusted_transform ( ) ) ;
}
}
2020-10-29 05:01:28 -05:00
TypedArray < String > XRCamera3D : : get_configuration_warnings ( ) const {
TypedArray < String > warnings = Node : : get_configuration_warnings ( ) ;
if ( is_visible ( ) & & is_inside_tree ( ) ) {
// must be child node of XROrigin3D!
XROrigin3D * origin = Object : : cast_to < XROrigin3D > ( get_parent ( ) ) ;
if ( origin = = nullptr ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " XRCamera3D must have an XROrigin3D node as its parent. " ) ) ;
2020-10-29 05:01:28 -05:00
} ;
2020-05-14 16:41:43 +02:00
}
2017-04-23 22:10:41 +10:00
2020-10-29 05:01:28 -05:00
return warnings ;
2017-04-23 22:10:41 +10:00
} ;
2020-04-09 00:47:36 +10:00
Vector3 XRCamera3D : : project_local_ray_normal ( const Point2 & p_pos ) const {
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL_V ( xr_server , Vector3 ( ) ) ;
2017-09-29 21:36:27 +10:00
2020-04-09 00:47:36 +10:00
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_null ( ) ) {
2018-06-24 11:54:08 +10:00
// we might be in the editor or have VR turned off, just call superclass
2020-03-26 18:49:16 -03:00
return Camera3D : : project_local_ray_normal ( p_pos ) ;
2018-06-24 11:54:08 +10:00
}
2017-09-29 21:36:27 +10:00
2019-08-08 22:11:48 +02:00
ERR_FAIL_COND_V_MSG ( ! is_inside_tree ( ) , Vector3 ( ) , " Camera is not inside scene. " ) ;
2017-09-29 21:36:27 +10:00
Size2 viewport_size = get_viewport ( ) - > get_camera_rect_size ( ) ;
Vector2 cpos = get_viewport ( ) - > get_camera_coords ( p_pos ) ;
Vector3 ray ;
2021-05-07 23:19:04 +10:00
// Just use the first view, if multiple views are supported this function has no good result
2022-07-20 01:11:13 +02:00
Projection cm = xr_interface - > get_projection_for_view ( 0 , viewport_size . aspect ( ) , get_near ( ) , get_far ( ) ) ;
2020-01-21 18:39:16 +00:00
Vector2 screen_he = cm . get_viewport_half_extents ( ) ;
2020-12-16 12:40:42 +00:00
ray = Vector3 ( ( ( cpos . x / viewport_size . width ) * 2.0 - 1.0 ) * screen_he . x , ( ( 1.0 - ( cpos . y / viewport_size . height ) ) * 2.0 - 1.0 ) * screen_he . y , - get_near ( ) ) . normalized ( ) ;
2017-09-29 21:36:27 +10:00
return ray ;
} ;
2020-04-09 00:47:36 +10:00
Point2 XRCamera3D : : unproject_position ( const Vector3 & p_pos ) const {
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL_V ( xr_server , Vector2 ( ) ) ;
2017-09-29 21:36:27 +10:00
2020-04-09 00:47:36 +10:00
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_null ( ) ) {
2018-06-24 11:54:08 +10:00
// we might be in the editor or have VR turned off, just call superclass
2020-03-26 18:49:16 -03:00
return Camera3D : : unproject_position ( p_pos ) ;
2018-06-24 11:54:08 +10:00
}
2017-09-29 21:36:27 +10:00
2019-08-08 22:11:48 +02:00
ERR_FAIL_COND_V_MSG ( ! is_inside_tree ( ) , Vector2 ( ) , " Camera is not inside scene. " ) ;
2017-09-29 21:36:27 +10:00
Size2 viewport_size = get_viewport ( ) - > get_visible_rect ( ) . size ;
2021-05-07 23:19:04 +10:00
// Just use the first view, if multiple views are supported this function has no good result
2022-07-20 01:11:13 +02:00
Projection cm = xr_interface - > get_projection_for_view ( 0 , viewport_size . aspect ( ) , get_near ( ) , get_far ( ) ) ;
2017-09-29 21:36:27 +10:00
Plane p ( get_camera_transform ( ) . xform_inv ( p_pos ) , 1.0 ) ;
p = cm . xform4 ( p ) ;
2020-05-10 16:47:11 +02:00
p . normal / = p . d ;
2017-09-29 21:36:27 +10:00
Point2 res ;
res . x = ( p . normal . x * 0.5 + 0.5 ) * viewport_size . x ;
res . y = ( - p . normal . y * 0.5 + 0.5 ) * viewport_size . y ;
return res ;
} ;
2021-01-29 19:55:54 -05:00
Vector3 XRCamera3D : : project_position ( const Point2 & p_point , real_t p_z_depth ) const {
2020-04-09 00:47:36 +10:00
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL_V ( xr_server , Vector3 ( ) ) ;
2017-09-29 21:36:27 +10:00
2020-04-09 00:47:36 +10:00
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_null ( ) ) {
2018-06-24 11:54:08 +10:00
// we might be in the editor or have VR turned off, just call superclass
2020-03-26 18:49:16 -03:00
return Camera3D : : project_position ( p_point , p_z_depth ) ;
2018-06-24 11:54:08 +10:00
}
2017-09-29 21:36:27 +10:00
2019-08-08 22:11:48 +02:00
ERR_FAIL_COND_V_MSG ( ! is_inside_tree ( ) , Vector3 ( ) , " Camera is not inside scene. " ) ;
2017-09-29 21:36:27 +10:00
Size2 viewport_size = get_viewport ( ) - > get_visible_rect ( ) . size ;
2021-05-07 23:19:04 +10:00
// Just use the first view, if multiple views are supported this function has no good result
2022-07-20 01:11:13 +02:00
Projection cm = xr_interface - > get_projection_for_view ( 0 , viewport_size . aspect ( ) , get_near ( ) , get_far ( ) ) ;
2017-09-29 21:36:27 +10:00
2020-01-21 18:39:16 +00:00
Vector2 vp_he = cm . get_viewport_half_extents ( ) ;
2017-09-29 21:36:27 +10:00
Vector2 point ;
point . x = ( p_point . x / viewport_size . x ) * 2.0 - 1.0 ;
point . y = ( 1.0 - ( p_point . y / viewport_size . y ) ) * 2.0 - 1.0 ;
2020-01-21 18:39:16 +00:00
point * = vp_he ;
2017-09-29 21:36:27 +10:00
2019-05-28 23:14:13 +10:00
Vector3 p ( point . x , point . y , - p_z_depth ) ;
2017-09-29 21:36:27 +10:00
return get_camera_transform ( ) . xform ( p ) ;
} ;
2020-04-09 00:47:36 +10:00
Vector < Plane > XRCamera3D : : get_frustum ( ) const {
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL_V ( xr_server , Vector < Plane > ( ) ) ;
2017-09-29 21:36:27 +10:00
2020-04-09 00:47:36 +10:00
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_null ( ) ) {
2018-06-24 11:54:08 +10:00
// we might be in the editor or have VR turned off, just call superclass
2020-03-26 18:49:16 -03:00
return Camera3D : : get_frustum ( ) ;
2018-06-24 11:54:08 +10:00
}
2017-09-29 21:36:27 +10:00
ERR_FAIL_COND_V ( ! is_inside_world ( ) , Vector < Plane > ( ) ) ;
Size2 viewport_size = get_viewport ( ) - > get_visible_rect ( ) . size ;
2021-05-07 23:19:04 +10:00
// TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
2022-07-20 01:11:13 +02:00
Projection cm = xr_interface - > get_projection_for_view ( 0 , viewport_size . aspect ( ) , get_near ( ) , get_far ( ) ) ;
2017-09-29 21:36:27 +10:00
return cm . get_projection_planes ( get_camera_transform ( ) ) ;
} ;
2021-08-29 16:05:11 +10:00
XRCamera3D : : XRCamera3D ( ) {
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
xr_server - > connect ( " tracker_added " , callable_mp ( this , & XRCamera3D : : _changed_tracker ) ) ;
xr_server - > connect ( " tracker_updated " , callable_mp ( this , & XRCamera3D : : _changed_tracker ) ) ;
xr_server - > connect ( " tracker_removed " , callable_mp ( this , & XRCamera3D : : _removed_tracker ) ) ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
XRCamera3D : : ~ XRCamera3D ( ) {
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
xr_server - > disconnect ( " tracker_added " , callable_mp ( this , & XRCamera3D : : _changed_tracker ) ) ;
xr_server - > disconnect ( " tracker_updated " , callable_mp ( this , & XRCamera3D : : _changed_tracker ) ) ;
xr_server - > disconnect ( " tracker_removed " , callable_mp ( this , & XRCamera3D : : _removed_tracker ) ) ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
////////////////////////////////////////////////////////////////////////////////////////////////////
// XRNode3D is a node that has it's transform updated by an XRPositionalTracker.
// Note that trackers are only available in runtime and only after an XRInterface registers one.
// So we bind by name and as long as a tracker isn't available, our node remains inactive.
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
void XRNode3D : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_tracker " , " tracker_name " ) , & XRNode3D : : set_tracker ) ;
ClassDB : : bind_method ( D_METHOD ( " get_tracker " ) , & XRNode3D : : get_tracker ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " tracker " , PROPERTY_HINT_ENUM_SUGGESTION ) , " set_tracker " , " get_tracker " ) ;
2017-11-01 21:46:37 +11:00
2021-08-29 16:05:11 +10:00
ClassDB : : bind_method ( D_METHOD ( " set_pose_name " , " pose " ) , & XRNode3D : : set_pose_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_pose_name " ) , & XRNode3D : : get_pose_name ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " pose " , PROPERTY_HINT_ENUM_SUGGESTION ) , " set_pose_name " , " get_pose_name " ) ;
2019-02-05 21:02:13 +11:00
2021-08-29 16:05:11 +10:00
ClassDB : : bind_method ( D_METHOD ( " get_is_active " ) , & XRNode3D : : get_is_active ) ;
ClassDB : : bind_method ( D_METHOD ( " get_has_tracking_data " ) , & XRNode3D : : get_has_tracking_data ) ;
ClassDB : : bind_method ( D_METHOD ( " get_pose " ) , & XRNode3D : : get_pose ) ;
ClassDB : : bind_method ( D_METHOD ( " trigger_haptic_pulse " , " action_name " , " frequency " , " amplitude " , " duration_sec " , " delay_sec " ) , & XRNode3D : : trigger_haptic_pulse ) ;
2017-04-23 22:10:41 +10:00
} ;
2022-08-12 23:57:11 +03:00
void XRNode3D : : _validate_property ( PropertyInfo & p_property ) const {
2021-08-29 16:05:11 +10:00
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
2017-04-23 22:10:41 +10:00
2022-08-12 23:57:11 +03:00
if ( p_property . name = = " tracker " ) {
2021-08-29 16:05:11 +10:00
PackedStringArray names = xr_server - > get_suggested_tracker_names ( ) ;
String hint_string ;
for ( const String & name : names ) {
hint_string + = name + " , " ;
}
2022-08-12 23:57:11 +03:00
p_property . hint_string = hint_string ;
} else if ( p_property . name = = " pose " ) {
2021-08-29 16:05:11 +10:00
PackedStringArray names = xr_server - > get_suggested_pose_names ( tracker_name ) ;
String hint_string ;
for ( const String & name : names ) {
hint_string + = name + " , " ;
}
2022-08-12 23:57:11 +03:00
p_property . hint_string = hint_string ;
2021-08-29 16:05:11 +10:00
}
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
void XRNode3D : : set_tracker ( const StringName p_tracker_name ) {
if ( tracker . is_valid ( ) & & tracker - > get_tracker_name ( ) = = p_tracker_name ) {
// didn't change
return ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
// just in case
_unbind_tracker ( ) ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
// copy the name
tracker_name = p_tracker_name ;
pose_name = " default " ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
// see if it's already available
_bind_tracker ( ) ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
update_configuration_warnings ( ) ;
notify_property_list_changed ( ) ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
StringName XRNode3D : : get_tracker ( ) const {
return tracker_name ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
void XRNode3D : : set_pose_name ( const StringName p_pose_name ) {
pose_name = p_pose_name ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
// Update pose if we are bound to a tracker with a valid pose
Ref < XRPose > pose = get_pose ( ) ;
if ( pose . is_valid ( ) ) {
set_transform ( pose - > get_adjusted_transform ( ) ) ;
}
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
StringName XRNode3D : : get_pose_name ( ) const {
return pose_name ;
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
bool XRNode3D : : get_is_active ( ) const {
if ( tracker . is_null ( ) ) {
return false ;
} else if ( ! tracker - > has_pose ( pose_name ) ) {
return false ;
} else {
return true ;
}
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
bool XRNode3D : : get_has_tracking_data ( ) const {
if ( tracker . is_null ( ) ) {
return false ;
} else if ( ! tracker - > has_pose ( pose_name ) ) {
return false ;
} else {
return tracker - > get_pose ( pose_name ) - > get_has_tracking_data ( ) ;
}
}
void XRNode3D : : trigger_haptic_pulse ( const String & p_action_name , double p_frequency , double p_amplitude , double p_duration_sec , double p_delay_sec ) {
// TODO need to link trackers to the interface that registered them so we can call this on the correct interface.
// For now this works fine as in 99% of the cases we only have our primary interface active
2020-04-09 00:47:36 +10:00
XRServer * xr_server = XRServer : : get_singleton ( ) ;
2021-08-29 16:05:11 +10:00
if ( xr_server ! = nullptr ) {
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_valid ( ) ) {
xr_interface - > trigger_haptic_pulse ( p_action_name , tracker_name , p_frequency , p_amplitude , p_duration_sec , p_delay_sec ) ;
}
}
}
2017-11-01 21:46:37 +11:00
2021-08-29 16:05:11 +10:00
Ref < XRPose > XRNode3D : : get_pose ( ) {
if ( tracker . is_valid ( ) ) {
return tracker - > get_pose ( pose_name ) ;
} else {
return Ref < XRPose > ( ) ;
}
}
2017-11-01 21:46:37 +11:00
2021-08-29 16:05:11 +10:00
void XRNode3D : : _bind_tracker ( ) {
ERR_FAIL_COND_MSG ( tracker . is_valid ( ) , " Unbind the current tracker first " ) ;
2017-11-01 21:46:37 +11:00
2020-04-09 00:47:36 +10:00
XRServer * xr_server = XRServer : : get_singleton ( ) ;
2021-08-29 16:05:11 +10:00
if ( xr_server ! = nullptr ) {
tracker = xr_server - > get_tracker ( tracker_name ) ;
if ( tracker . is_null ( ) ) {
// It is possible and valid if the tracker isn't available (yet), in this case we just exit
return ;
}
tracker - > connect ( " pose_changed " , callable_mp ( this , & XRNode3D : : _pose_changed ) ) ;
Ref < XRPose > pose = get_pose ( ) ;
if ( pose . is_valid ( ) ) {
set_transform ( pose - > get_adjusted_transform ( ) ) ;
}
}
}
2017-11-01 21:46:37 +11:00
2021-08-29 16:05:11 +10:00
void XRNode3D : : _unbind_tracker ( ) {
2021-03-29 21:15:53 +11:00
if ( tracker . is_valid ( ) ) {
2021-08-29 16:05:11 +10:00
tracker - > disconnect ( " pose_changed " , callable_mp ( this , & XRNode3D : : _pose_changed ) ) ;
2017-11-01 21:46:37 +11:00
2021-08-29 16:05:11 +10:00
tracker . unref ( ) ;
}
2019-02-05 21:02:13 +11:00
}
2021-08-29 16:05:11 +10:00
void XRNode3D : : _changed_tracker ( const StringName p_tracker_name , int p_tracker_type ) {
if ( p_tracker_name = = p_tracker_name ) {
// just in case unref our current tracker
_unbind_tracker ( ) ;
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
// get our new tracker
_bind_tracker ( ) ;
}
}
void XRNode3D : : _removed_tracker ( const StringName p_tracker_name , int p_tracker_type ) {
if ( p_tracker_name = = p_tracker_name ) {
// unref our tracker, it's no longer available
_unbind_tracker ( ) ;
}
}
void XRNode3D : : _pose_changed ( const Ref < XRPose > & p_pose ) {
if ( p_pose . is_valid ( ) & & p_pose - > get_name ( ) = = pose_name ) {
set_transform ( p_pose - > get_adjusted_transform ( ) ) ;
}
}
XRNode3D : : XRNode3D ( ) {
2020-04-09 00:47:36 +10:00
XRServer * xr_server = XRServer : : get_singleton ( ) ;
2021-08-29 16:05:11 +10:00
ERR_FAIL_NULL ( xr_server ) ;
2017-09-10 16:15:11 +10:00
2021-08-29 16:05:11 +10:00
xr_server - > connect ( " tracker_added " , callable_mp ( this , & XRNode3D : : _changed_tracker ) ) ;
xr_server - > connect ( " tracker_updated " , callable_mp ( this , & XRNode3D : : _changed_tracker ) ) ;
xr_server - > connect ( " tracker_removed " , callable_mp ( this , & XRNode3D : : _removed_tracker ) ) ;
}
2017-09-10 16:15:11 +10:00
2021-08-29 16:05:11 +10:00
XRNode3D : : ~ XRNode3D ( ) {
_unbind_tracker ( ) ;
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
xr_server - > disconnect ( " tracker_added " , callable_mp ( this , & XRNode3D : : _changed_tracker ) ) ;
xr_server - > disconnect ( " tracker_updated " , callable_mp ( this , & XRNode3D : : _changed_tracker ) ) ;
xr_server - > disconnect ( " tracker_removed " , callable_mp ( this , & XRNode3D : : _removed_tracker ) ) ;
}
2017-09-10 16:15:11 +10:00
2021-08-29 16:05:11 +10:00
TypedArray < String > XRNode3D : : get_configuration_warnings ( ) const {
2020-10-29 05:01:28 -05:00
TypedArray < String > warnings = Node : : get_configuration_warnings ( ) ;
2020-05-14 23:59:27 +03:00
2020-10-29 05:01:28 -05:00
if ( is_visible ( ) & & is_inside_tree ( ) ) {
// must be child node of XROrigin!
XROrigin3D * origin = Object : : cast_to < XROrigin3D > ( get_parent ( ) ) ;
if ( origin = = nullptr ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " XRController3D must have an XROrigin3D node as its parent. " ) ) ;
2020-05-14 23:59:27 +03:00
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
if ( tracker_name = = " " ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " No tracker name is set. " ) ) ;
2021-08-29 16:05:11 +10:00
}
if ( pose_name = = " " ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " No pose is set. " ) ) ;
2020-05-14 23:59:27 +03:00
}
2020-10-29 05:01:28 -05:00
}
2017-04-23 22:10:41 +10:00
2020-10-29 05:01:28 -05:00
return warnings ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2021-08-29 16:05:11 +10:00
void XRController3D : : _bind_methods ( ) {
// passthroughs to information about our related joystick
ClassDB : : bind_method ( D_METHOD ( " is_button_pressed " , " name " ) , & XRController3D : : is_button_pressed ) ;
ClassDB : : bind_method ( D_METHOD ( " get_value " , " name " ) , & XRController3D : : get_value ) ;
ClassDB : : bind_method ( D_METHOD ( " get_axis " , " name " ) , & XRController3D : : get_axis ) ;
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
ClassDB : : bind_method ( D_METHOD ( " get_tracker_hand " ) , & XRController3D : : get_tracker_hand ) ;
2017-09-29 21:36:27 +10:00
2021-08-29 16:05:11 +10:00
ADD_SIGNAL ( MethodInfo ( " button_pressed " , PropertyInfo ( Variant : : STRING , " name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " button_released " , PropertyInfo ( Variant : : STRING , " name " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " input_value_changed " , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : FLOAT , " value " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " input_axis_changed " , PropertyInfo ( Variant : : STRING , " name " ) , PropertyInfo ( Variant : : VECTOR2 , " value " ) ) ) ;
2017-08-03 18:58:05 +10:00
} ;
2021-08-29 16:05:11 +10:00
void XRController3D : : _bind_tracker ( ) {
XRNode3D : : _bind_tracker ( ) ;
if ( tracker . is_valid ( ) ) {
// bind to input signals
tracker - > connect ( " button_pressed " , callable_mp ( this , & XRController3D : : _button_pressed ) ) ;
tracker - > connect ( " button_released " , callable_mp ( this , & XRController3D : : _button_released ) ) ;
tracker - > connect ( " input_value_changed " , callable_mp ( this , & XRController3D : : _input_value_changed ) ) ;
tracker - > connect ( " input_axis_changed " , callable_mp ( this , & XRController3D : : _input_axis_changed ) ) ;
}
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
void XRController3D : : _unbind_tracker ( ) {
if ( tracker . is_valid ( ) ) {
// unbind input signals
tracker - > disconnect ( " button_pressed " , callable_mp ( this , & XRController3D : : _button_pressed ) ) ;
tracker - > disconnect ( " button_released " , callable_mp ( this , & XRController3D : : _button_released ) ) ;
tracker - > disconnect ( " input_value_changed " , callable_mp ( this , & XRController3D : : _input_value_changed ) ) ;
tracker - > disconnect ( " input_axis_changed " , callable_mp ( this , & XRController3D : : _input_axis_changed ) ) ;
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
XRNode3D : : _unbind_tracker ( ) ;
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
void XRController3D : : _button_pressed ( const String & p_name ) {
// just pass it on...
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " button_pressed " ) , p_name ) ;
2021-08-29 16:05:11 +10:00
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
void XRController3D : : _button_released ( const String & p_name ) {
// just pass it on...
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " button_released " ) , p_name ) ;
2021-08-29 16:05:11 +10:00
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
void XRController3D : : _input_value_changed ( const String & p_name , float p_value ) {
// just pass it on...
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " input_value_changed " ) , p_name , p_value ) ;
2021-08-29 16:05:11 +10:00
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
void XRController3D : : _input_axis_changed ( const String & p_name , Vector2 p_value ) {
// just pass it on...
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " input_axis_changed " ) , p_name , p_value ) ;
2021-08-29 16:05:11 +10:00
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
bool XRController3D : : is_button_pressed ( const StringName & p_name ) const {
if ( tracker . is_valid ( ) ) {
// Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type
bool pressed = tracker - > get_input ( p_name ) ;
return pressed ;
} else {
return false ;
}
}
2020-05-14 23:59:27 +03:00
2021-08-29 16:05:11 +10:00
float XRController3D : : get_value ( const StringName & p_name ) const {
if ( tracker . is_valid ( ) ) {
// Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert
Variant input = tracker - > get_input ( p_name ) ;
switch ( input . get_type ( ) ) {
case Variant : : BOOL : {
bool value = input ;
return value ? 1.0 : 0.0 ;
} break ;
case Variant : : FLOAT : {
float value = input ;
return value ;
} break ;
default :
return 0.0 ;
} ;
} else {
return 0.0 ;
}
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
Vector2 XRController3D : : get_axis ( const StringName & p_name ) const {
if ( tracker . is_valid ( ) ) {
// Inputs should already be of the correct type, our XR runtime handles conversions between raw input and the desired type, but just in case we convert
Variant input = tracker - > get_input ( p_name ) ;
switch ( input . get_type ( ) ) {
case Variant : : BOOL : {
bool value = input ;
return Vector2 ( value ? 1.0 : 0.0 , 0.0 ) ;
} break ;
case Variant : : FLOAT : {
float value = input ;
return Vector2 ( value , 0.0 ) ;
} break ;
case Variant : : VECTOR2 : {
Vector2 axis = input ;
return axis ;
}
default :
return Vector2 ( ) ;
2020-05-14 23:59:27 +03:00
}
2021-08-29 16:05:11 +10:00
} else {
return Vector2 ( ) ;
2020-10-29 05:01:28 -05:00
}
2021-08-29 16:05:11 +10:00
}
2017-08-03 18:58:05 +10:00
2021-08-29 16:05:11 +10:00
XRPositionalTracker : : TrackerHand XRController3D : : get_tracker_hand ( ) const {
// get our XRServer
if ( ! tracker . is_valid ( ) ) {
return XRPositionalTracker : : TRACKER_HAND_UNKNOWN ;
}
return tracker - > get_tracker_hand ( ) ;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void XRAnchor3D : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " get_size " ) , & XRAnchor3D : : get_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_plane " ) , & XRAnchor3D : : get_plane ) ;
}
Vector3 XRAnchor3D : : get_size ( ) const {
return size ;
}
2017-08-03 18:58:05 +10:00
2020-04-09 00:47:36 +10:00
Plane XRAnchor3D : : get_plane ( ) const {
2020-12-08 17:35:30 +00:00
Vector3 location = get_position ( ) ;
2017-09-29 21:36:27 +10:00
Basis orientation = get_transform ( ) . basis ;
2022-05-03 07:50:35 -05:00
Plane plane ( orientation . get_column ( 1 ) . normalized ( ) , location ) ;
2017-09-29 21:36:27 +10:00
return plane ;
2019-02-05 21:02:13 +11:00
}
2017-08-03 18:58:05 +10:00
////////////////////////////////////////////////////////////////////////////////////////////////////
2020-10-29 05:01:28 -05:00
TypedArray < String > XROrigin3D : : get_configuration_warnings ( ) const {
TypedArray < String > warnings = Node : : get_configuration_warnings ( ) ;
2020-05-14 23:59:27 +03:00
2020-10-29 05:01:28 -05:00
if ( is_visible ( ) & & is_inside_tree ( ) ) {
if ( tracked_camera = = nullptr ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " XROrigin3D requires an XRCamera3D child node. " ) ) ;
2020-05-14 23:59:27 +03:00
}
2020-05-14 16:41:43 +02:00
}
2017-04-23 22:10:41 +10:00
2021-12-14 12:44:12 +11:00
bool xr_enabled = GLOBAL_GET ( " xr/shaders/enabled " ) ;
2021-06-14 11:05:16 +10:00
if ( ! xr_enabled ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled. " ) ) ;
2021-05-07 23:19:04 +10:00
}
2020-10-29 05:01:28 -05:00
return warnings ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
2020-04-09 00:47:36 +10:00
void XROrigin3D : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_world_scale " , " world_scale " ) , & XROrigin3D : : set_world_scale ) ;
ClassDB : : bind_method ( D_METHOD ( " get_world_scale " ) , & XROrigin3D : : get_world_scale ) ;
2020-02-24 15:20:53 -03:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " world_scale " ) , " set_world_scale " , " get_world_scale " ) ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
2020-04-09 00:47:36 +10:00
void XROrigin3D : : set_tracked_camera ( XRCamera3D * p_tracked_camera ) {
2017-04-23 22:10:41 +10:00
tracked_camera = p_tracked_camera ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
2021-08-29 16:05:11 +10:00
XRCamera3D * XROrigin3D : : get_tracked_camera ( ) const {
return tracked_camera ;
}
2017-04-23 22:10:41 +10:00
2021-01-29 19:55:54 -05:00
real_t XROrigin3D : : get_world_scale ( ) const {
2020-04-09 00:47:36 +10:00
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL_V ( xr_server , 1.0 ) ;
2017-04-23 22:10:41 +10:00
2020-04-09 00:47:36 +10:00
return xr_server - > get_world_scale ( ) ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
2021-01-29 19:55:54 -05:00
void XROrigin3D : : set_world_scale ( real_t p_world_scale ) {
2020-04-09 00:47:36 +10:00
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
2017-04-23 22:10:41 +10:00
2020-04-09 00:47:36 +10:00
xr_server - > set_world_scale ( p_world_scale ) ;
2021-08-29 16:05:11 +10:00
}
2017-04-23 22:10:41 +10:00
2020-04-09 00:47:36 +10:00
void XROrigin3D : : _notification ( int p_what ) {
// get our XRServer
XRServer * xr_server = XRServer : : get_singleton ( ) ;
ERR_FAIL_NULL ( xr_server ) ;
2019-03-13 20:43:21 +11:00
2017-04-23 22:10:41 +10:00
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
set_process_internal ( true ) ;
2022-02-15 18:06:48 +01:00
} break ;
2017-04-23 22:10:41 +10:00
case NOTIFICATION_EXIT_TREE : {
set_process_internal ( false ) ;
2022-02-15 18:06:48 +01:00
} break ;
2017-04-23 22:10:41 +10:00
case NOTIFICATION_INTERNAL_PROCESS : {
// set our world origin to our node transform
2020-04-09 00:47:36 +10:00
xr_server - > set_world_origin ( get_global_transform ( ) ) ;
2017-04-23 22:10:41 +10:00
// check if we have a primary interface
2020-04-09 00:47:36 +10:00
Ref < XRInterface > xr_interface = xr_server - > get_primary_interface ( ) ;
if ( xr_interface . is_valid ( ) & & tracked_camera ! = nullptr ) {
2017-04-23 22:10:41 +10:00
// get our positioning transform for our headset
2021-05-07 23:19:04 +10:00
Transform3D t = xr_interface - > get_camera_transform ( ) ;
2017-04-23 22:10:41 +10:00
// now apply this to our camera
tracked_camera - > set_transform ( t ) ;
2022-02-15 18:06:48 +01:00
}
} break ;
}
2019-03-13 20:43:21 +11:00
2020-04-09 00:47:36 +10:00
// send our notification to all active XE interfaces, they may need to react to it also
for ( int i = 0 ; i < xr_server - > get_interface_count ( ) ; i + + ) {
Ref < XRInterface > interface = xr_server - > get_interface ( i ) ;
2019-03-13 20:43:21 +11:00
if ( interface . is_valid ( ) & & interface - > is_initialized ( ) ) {
interface - > notification ( p_what ) ;
}
}
2021-08-29 16:05:11 +10:00
}