Change hand tracking project settings and finetune show_when_tracked

This commit is contained in:
Bastiaan Olij 2024-08-05 15:39:27 +10:00
parent da5f39889f
commit a8c5117777
9 changed files with 96 additions and 32 deletions

View File

@ -2947,10 +2947,19 @@
Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed. Specify whether to enable eye tracking for this project. Depending on the platform, additional export configuration may be needed.
</member> </member>
<member name="xr/openxr/extensions/hand_interaction_profile" type="bool" setter="" getter="" default="false"> <member name="xr/openxr/extensions/hand_interaction_profile" type="bool" setter="" getter="" default="false">
If true the hand interaction profile extension will be activated if supported by the platform. If [code]true[/code] the hand interaction profile extension will be activated if supported by the platform.
</member> </member>
<member name="xr/openxr/extensions/hand_tracking" type="bool" setter="" getter="" default="true"> <member name="xr/openxr/extensions/hand_tracking" type="bool" setter="" getter="" default="false">
If true we enable the hand tracking extension if available. If [code]true[/code], the hand tracking extension is enabled if available.
[b]Note:[/b] By default hand tracking will only work for data sources chosen by the XR runtime. For SteamVR this is the controller inferred data source, for most other runtimes this is the unobstructed data source. There is no way to query this. If a runtime supports the OpenXR data source extension you can use the [member xr/openxr/extensions/hand_tracking_controller_data_source] and/or [member xr/openxr/extensions/hand_tracking_unobstructed_data_source] to indicate you wish to enable these data sources. If neither is selected the data source extension is not enabled and the XR runtimes default behavior persists.
</member>
<member name="xr/openxr/extensions/hand_tracking_controller_data_source" type="bool" setter="" getter="" default="false">
If [code]true[/code], support for the controller inferred data source is requested. If supported, you will receive hand tracking data even if the user has a controller in hand, with finger positions automatically inferred from controller input and/or sensors.
[b]Node:[/b] This requires the OpenXR data source extension and controller inferred handtracking to be supported by the XR runtime. If not supported this setting will be ignored. [member xr/openxr/extensions/hand_tracking] must be enabled for this setting to be used.
</member>
<member name="xr/openxr/extensions/hand_tracking_unobstructed_data_source" type="bool" setter="" getter="" default="false">
If [code]true[/code], support for the unobstructed data source is requested. If supported, you will receive hand tracking data based on the actual finger positions of the user often determined by optical tracking.
[b]Node:[/b] This requires the OpenXR data source extension and unobstructed handtracking to be supported by the XR runtime. If not supported this setting will be ignored. [member xr/openxr/extensions/hand_tracking] must be enabled for this setting to be used.
</member> </member>
<member name="xr/openxr/form_factor" type="int" setter="" getter="" default="&quot;0&quot;"> <member name="xr/openxr/form_factor" type="int" setter="" getter="" default="&quot;0&quot;">
Specify whether OpenXR should be configured for an HMD or a hand held device. Specify whether OpenXR should be configured for an HMD or a hand held device.

View File

@ -107,7 +107,10 @@
<constant name="HAND_TRACKING_SOURCE_CONTROLLER" value="2" enum="HandTrackingSource"> <constant name="HAND_TRACKING_SOURCE_CONTROLLER" value="2" enum="HandTrackingSource">
The source of hand tracking data is a controller, meaning that joint positions are inferred from controller inputs. The source of hand tracking data is a controller, meaning that joint positions are inferred from controller inputs.
</constant> </constant>
<constant name="HAND_TRACKING_SOURCE_MAX" value="3" enum="HandTrackingSource"> <constant name="HAND_TRACKING_SOURCE_NOT_TRACKED" value="3" enum="HandTrackingSource">
No hand tracking data is tracked, this either means the hand is obscured, the controller is turned off, or tracking is not supported for the current input type.
</constant>
<constant name="HAND_TRACKING_SOURCE_MAX" value="4" enum="HandTrackingSource">
Represents the size of the [enum HandTrackingSource] enum. Represents the size of the [enum HandTrackingSource] enum.
</constant> </constant>
<constant name="HAND_JOINT_PALM" value="0" enum="HandJoint"> <constant name="HAND_JOINT_PALM" value="0" enum="HandJoint">

View File

@ -2512,7 +2512,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true); GLOBAL_DEF_BASIC("xr/openxr/startup_alert", true);
// OpenXR project extensions settings. // OpenXR project extensions settings.
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", true); GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_unobstructed_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT
GLOBAL_DEF_BASIC("xr/openxr/extensions/hand_tracking_controller_data_source", false); // XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT
GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false); GLOBAL_DEF_RST_BASIC("xr/openxr/extensions/hand_interaction_profile", false);
GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false); GLOBAL_DEF_BASIC("xr/openxr/extensions/eye_gaze_interaction", false);

View File

@ -58,9 +58,14 @@ OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() { HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() {
HashMap<String, bool *> request_extensions; HashMap<String, bool *> request_extensions;
unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source");
controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source");
request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext; request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext;
request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext; request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext;
if (unobstructed_data_source || controller_data_source) {
request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext; request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
}
return request_extensions; return request_extensions;
} }
@ -141,10 +146,18 @@ void OpenXRHandTrackingExtension::on_process() {
void *next_pointer = nullptr; void *next_pointer = nullptr;
// Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking. // Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking.
// With this extension we can indicate we accept input from both sources so hand tracking data is consistently provided // With this extension we can indicate we wish to accept input from either or both sources.
// on runtimes that support this. // This functionality is subject to the abilities of the XR runtime and requires the data source extension.
XrHandTrackingDataSourceEXT data_sources[2] = { XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT }; // Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports.
XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, 2, data_sources }; uint32_t data_source_count = 0;
XrHandTrackingDataSourceEXT data_sources[2];
if (unobstructed_data_source) {
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT;
}
if (controller_data_source) {
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT;
}
XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources };
if (hand_tracking_source_ext) { if (hand_tracking_source_ext) {
// If supported include this info // If supported include this info
next_pointer = &data_source_info; next_pointer = &data_source_info;
@ -224,7 +237,9 @@ void OpenXRHandTrackingExtension::on_process() {
if (XR_FAILED(result)) { if (XR_FAILED(result)) {
// not successful? then we do nothing. // not successful? then we do nothing.
print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]"); print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false); godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default");
continue; continue;
} }
@ -235,8 +250,6 @@ void OpenXRHandTrackingExtension::on_process() {
} }
if (hand_trackers[i].locations.isActive) { if (hand_trackers[i].locations.isActive) {
godot_tracker->set_has_tracking_data(true);
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs: // SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone) // OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand) // OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
@ -245,7 +258,6 @@ void OpenXRHandTrackingExtension::on_process() {
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) { for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint]; const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint];
const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint]; const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint];
const XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
const XrPosef &pose = location.pose; const XrPosef &pose = location.pose;
Transform3D transform; Transform3D transform;
@ -285,23 +297,35 @@ void OpenXRHandTrackingExtension::on_process() {
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius); godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
if (joint == XR_HAND_JOINT_PALM_EXT) { if (joint == XR_HAND_JOINT_PALM_EXT) {
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN; XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) { if (hand_tracking_source_ext) {
if (!data_source.isActive) {
source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED;
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED; source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { } else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER; source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
} else {
// Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
WARN_PRINT_ONCE("Unknown active data source found!");
source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
}
} }
godot_tracker->set_hand_tracking_source(source); godot_tracker->set_hand_tracking_source(source);
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) { godot_tracker->set_has_tracking_data(true);
godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity); godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
} else { } else {
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false); godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default"); godot_tracker->invalidate_pose("default");
} }
} }
} }
} else { } else {
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
godot_tracker->set_has_tracking_data(false); godot_tracker->set_has_tracking_data(false);
godot_tracker->invalidate_pose("default"); godot_tracker->invalidate_pose("default");
} }
@ -349,15 +373,16 @@ XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTra
OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const { OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const {
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN); ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN);
if (hand_tracking_source_ext && hand_trackers[p_hand].data_source.isActive) { if (hand_tracking_source_ext) {
switch (hand_trackers[p_hand].data_source.dataSource) { if (!hand_trackers[p_hand].data_source.isActive) {
case XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT: return OPENXR_SOURCE_NOT_TRACKED;
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
return OPENXR_SOURCE_UNOBSTRUCTED; return OPENXR_SOURCE_UNOBSTRUCTED;
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
case XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT:
return OPENXR_SOURCE_CONTROLLER; return OPENXR_SOURCE_CONTROLLER;
} else {
default: // Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
WARN_PRINT_ONCE("Unknown active data source found!");
return OPENXR_SOURCE_UNKNOWN; return OPENXR_SOURCE_UNKNOWN;
} }
} }

View File

@ -48,6 +48,7 @@ public:
OPENXR_SOURCE_UNKNOWN, OPENXR_SOURCE_UNKNOWN,
OPENXR_SOURCE_UNOBSTRUCTED, OPENXR_SOURCE_UNOBSTRUCTED,
OPENXR_SOURCE_CONTROLLER, OPENXR_SOURCE_CONTROLLER,
OPENXR_SOURCE_NOT_TRACKED,
OPENXR_SOURCE_MAX OPENXR_SOURCE_MAX
}; };
@ -110,6 +111,8 @@ private:
bool hand_tracking_ext = false; bool hand_tracking_ext = false;
bool hand_motion_range_ext = false; bool hand_motion_range_ext = false;
bool hand_tracking_source_ext = false; bool hand_tracking_source_ext = false;
bool unobstructed_data_source = false;
bool controller_data_source = false;
// functions // functions
void cleanup_hand_tracking(); void cleanup_hand_tracking();

View File

@ -303,6 +303,8 @@ StringName XRNode3D::get_pose_name() const {
void XRNode3D::set_show_when_tracked(bool p_show) { void XRNode3D::set_show_when_tracked(bool p_show) {
show_when_tracked = p_show; show_when_tracked = p_show;
_update_visibility();
} }
bool XRNode3D::get_show_when_tracked() const { bool XRNode3D::get_show_when_tracked() const {
@ -361,6 +363,9 @@ void XRNode3D::_bind_tracker() {
if (pose.is_valid()) { if (pose.is_valid()) {
set_transform(pose->get_adjusted_transform()); set_transform(pose->get_adjusted_transform());
_set_has_tracking_data(pose->get_has_tracking_data()); _set_has_tracking_data(pose->get_has_tracking_data());
} else {
// Pose has been invalidated or was never set.
_set_has_tracking_data(false);
} }
} }
} }
@ -407,6 +412,10 @@ void XRNode3D::_pose_lost_tracking(const Ref<XRPose> &p_pose) {
} }
void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) { void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) {
// Always update our visibility, we may have set our tracking data
// when conditions weren't right.
_update_visibility();
// Ignore if the has_tracking_data state isn't changing. // Ignore if the has_tracking_data state isn't changing.
if (p_has_tracking_data == has_tracking_data) { if (p_has_tracking_data == has_tracking_data) {
return; return;
@ -415,11 +424,20 @@ void XRNode3D::_set_has_tracking_data(bool p_has_tracking_data) {
// Handle change of has_tracking_data. // Handle change of has_tracking_data.
has_tracking_data = p_has_tracking_data; has_tracking_data = p_has_tracking_data;
emit_signal(SNAME("tracking_changed"), has_tracking_data); emit_signal(SNAME("tracking_changed"), has_tracking_data);
}
void XRNode3D::_update_visibility() {
// If configured, show or hide the node based on tracking data. // If configured, show or hide the node based on tracking data.
if (show_when_tracked) { if (show_when_tracked) {
// Only react to this if we have a primary interface.
XRServer *xr_server = XRServer::get_singleton();
if (xr_server != nullptr) {
Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
if (xr_interface.is_valid()) {
set_visible(has_tracking_data); set_visible(has_tracking_data);
} }
}
}
} }
XRNode3D::XRNode3D() { XRNode3D::XRNode3D() {

View File

@ -95,6 +95,8 @@ protected:
void _pose_lost_tracking(const Ref<XRPose> &p_pose); void _pose_lost_tracking(const Ref<XRPose> &p_pose);
void _set_has_tracking_data(bool p_has_tracking_data); void _set_has_tracking_data(bool p_has_tracking_data);
void _update_visibility();
public: public:
void _validate_property(PropertyInfo &p_property) const; void _validate_property(PropertyInfo &p_property) const;
void set_tracker(const StringName &p_tracker_name); void set_tracker(const StringName &p_tracker_name);

View File

@ -60,6 +60,7 @@ void XRHandTracker::_bind_methods() {
BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNKNOWN);
BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_UNOBSTRUCTED);
BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_CONTROLLER);
BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_NOT_TRACKED);
BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_MAX); BIND_ENUM_CONSTANT(HAND_TRACKING_SOURCE_MAX);
BIND_ENUM_CONSTANT(HAND_JOINT_PALM); BIND_ENUM_CONSTANT(HAND_JOINT_PALM);

View File

@ -42,6 +42,7 @@ public:
HAND_TRACKING_SOURCE_UNKNOWN, HAND_TRACKING_SOURCE_UNKNOWN,
HAND_TRACKING_SOURCE_UNOBSTRUCTED, HAND_TRACKING_SOURCE_UNOBSTRUCTED,
HAND_TRACKING_SOURCE_CONTROLLER, HAND_TRACKING_SOURCE_CONTROLLER,
HAND_TRACKING_SOURCE_NOT_TRACKED,
HAND_TRACKING_SOURCE_MAX HAND_TRACKING_SOURCE_MAX
}; };