diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index f0ca649fe4f..8d8cbf1a293 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -23,6 +23,53 @@
Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized.
+
+
+
+
+
+ If handtracking is enabled, returns the angular velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D]!
+
+
+
+
+
+
+
+ If handtracking is enabled, returns the linear velocity of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
+
+
+
+
+
+
+
+ If handtracking is enabled, returns the position of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is relative to [XROrigin3D] without worldscale applied!
+
+
+
+
+
+
+
+ If handtracking is enabled, returns the radius of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR. This is without worldscale applied!
+
+
+
+
+
+
+
+ If handtracking is enabled, returns the rotation of a joint ([param joint]) of a hand ([param hand]) as provided by OpenXR.
+
+
+
+
+
+
+ If handtracking is enabled and motion range is supported, gets the currently configured motion range for [param hand].
+
+
@@ -38,6 +85,14 @@
Sets the given action set as active or inactive.
+
+
+
+
+
+ If handtracking is enabled and motion range is supported, sets the currently configured motion range for [param hand] to [param motion_range].
+
+
@@ -74,4 +129,102 @@
+
+
+ Left hand.
+
+
+ Right hand.
+
+
+ Maximum value for the hand enum.
+
+
+
+
+
+
+
+
+ Palm joint.
+
+
+ Wrist joint.
+
+
+ Thumb metacarpal joint.
+
+
+ Thumb proximal joint.
+
+
+ Thumb distal joint.
+
+
+ Thumb tip joint.
+
+
+ Index metacarpal joint.
+
+
+ Index proximal joint.
+
+
+ Index intermediate joint.
+
+
+ Index distal joint.
+
+
+ Index tip joint.
+
+
+ Middle metacarpal joint.
+
+
+ Middle proximal joint.
+
+
+ Middle intermediate joint.
+
+
+ Middle distal joint.
+
+
+ Middle tip joint.
+
+
+ Ring metacarpal joint.
+
+
+ Ring proximal joint.
+
+
+ Ring intermediate joint.
+
+
+ Ring distal joint.
+
+
+ Ring tip joint.
+
+
+ Little metacarpal joint.
+
+
+ Little proximal joint.
+
+
+ Little intermediate joint.
+
+
+ Little distal joint.
+
+
+ Little tip joint.
+
+
+ Maximum value for the hand joint enum.
+
+
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
index 65559afed02..c92b2b08d0e 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
@@ -276,3 +276,62 @@ void OpenXRHandTrackingExtension::set_motion_range(uint32_t p_hand, XrHandJoints
ERR_FAIL_UNSIGNED_INDEX(p_hand, MAX_OPENXR_TRACKED_HANDS);
hand_trackers[p_hand].motion_range = p_motion_range;
}
+
+Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(uint32_t p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Quaternion());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion());
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return Quaternion();
+ }
+
+ const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
+ return Quaternion(location.pose.orientation.x, location.pose.orientation.y, location.pose.orientation.z, location.pose.orientation.w);
+}
+
+Vector3 OpenXRHandTrackingExtension::get_hand_joint_position(uint32_t p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return Vector3();
+ }
+
+ const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
+ return Vector3(location.pose.position.x, location.pose.position.y, location.pose.position.z);
+}
+
+float OpenXRHandTrackingExtension::get_hand_joint_radius(uint32_t p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, 0.0);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, 0.0);
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return 0.0;
+ }
+
+ return hand_trackers[p_hand].joint_locations[p_joint].radius;
+}
+
+Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return Vector3();
+ }
+
+ const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
+ return Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
+}
+
+Vector3 OpenXRHandTrackingExtension::get_hand_joint_angular_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3());
+ ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
+
+ if (!hand_trackers[p_hand].is_initialized) {
+ return Vector3();
+ }
+
+ const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
+ return Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
+}
diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.h b/modules/openxr/extensions/openxr_hand_tracking_extension.h
index e86831e71bf..99d315c5255 100644
--- a/modules/openxr/extensions/openxr_hand_tracking_extension.h
+++ b/modules/openxr/extensions/openxr_hand_tracking_extension.h
@@ -32,6 +32,7 @@
#define OPENXR_HAND_TRACKING_EXTENSION_H
#include "../util.h"
+#include "core/math/quaternion.h"
#include "openxr_extension_wrapper.h"
#define MAX_OPENXR_TRACKED_HANDS 2
@@ -73,6 +74,13 @@ public:
XrHandJointsMotionRangeEXT get_motion_range(uint32_t p_hand) const;
void set_motion_range(uint32_t p_hand, XrHandJointsMotionRangeEXT p_motion_range);
+ Quaternion get_hand_joint_rotation(uint32_t p_hand, XrHandJointEXT p_joint) const;
+ Vector3 get_hand_joint_position(uint32_t p_hand, XrHandJointEXT p_joint) const;
+ float get_hand_joint_radius(uint32_t p_hand, XrHandJointEXT p_joint) const;
+
+ Vector3 get_hand_joint_linear_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const;
+ Vector3 get_hand_joint_angular_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const;
+
private:
static OpenXRHandTrackingExtension *singleton;
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index cc2b4fa11bc..4dda51147be 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -34,6 +34,8 @@
#include "core/io/resource_saver.h"
#include "servers/rendering/rendering_server_globals.h"
+#include "extensions/openxr_hand_tracking_extension.h"
+
void OpenXRInterface::_bind_methods() {
// lifecycle signals
ADD_SIGNAL(MethodInfo("session_begun"));
@@ -57,6 +59,53 @@ void OpenXRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets);
ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);
+
+ // Hand tracking.
+ ClassDB::bind_method(D_METHOD("set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range);
+ ClassDB::bind_method(D_METHOD("get_motion_range", "hand"), &OpenXRInterface::get_motion_range);
+
+ ClassDB::bind_method(D_METHOD("get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius);
+
+ ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);
+
+ BIND_ENUM_CONSTANT(HAND_LEFT);
+ BIND_ENUM_CONSTANT(HAND_RIGHT);
+ BIND_ENUM_CONSTANT(HAND_MAX);
+
+ BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_UNOBSTRUCTED);
+ BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER);
+ BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_MAX);
+
+ BIND_ENUM_CONSTANT(HAND_JOINT_PALM);
+ BIND_ENUM_CONSTANT(HAND_JOINT_WRIST);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_RING_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_METACARPAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_PROXIMAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_INTERMEDIATE);
+ BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_DISTAL);
+ BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP);
+ BIND_ENUM_CONSTANT(HAND_JOINT_MAX);
}
StringName OpenXRInterface::get_name() const {
@@ -978,6 +1027,96 @@ void OpenXRInterface::on_pose_recentered() {
emit_signal(SNAME("pose_recentered"));
}
+/** Hand tracking. */
+void OpenXRInterface::set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range) {
+ ERR_FAIL_INDEX(p_hand, HAND_MAX);
+ ERR_FAIL_INDEX(p_motion_range, HAND_MOTION_RANGE_MAX);
+
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ XrHandJointsMotionRangeEXT xr_motion_range;
+ switch (p_motion_range) {
+ case HAND_MOTION_RANGE_UNOBSTRUCTED:
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
+ break;
+ case HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER:
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
+ break;
+ default:
+ // Shouldn't get here, ERR_FAIL_INDEX should have caught this...
+ xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
+ break;
+ }
+
+ hand_tracking_ext->set_motion_range(uint32_t(p_hand), xr_motion_range);
+ }
+}
+
+OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_hand) const {
+ ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_MOTION_RANGE_MAX);
+
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ XrHandJointsMotionRangeEXT xr_motion_range = hand_tracking_ext->get_motion_range(uint32_t(p_hand));
+
+ switch (xr_motion_range) {
+ case XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT:
+ return HAND_MOTION_RANGE_UNOBSTRUCTED;
+ case XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT:
+ return HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER;
+ default:
+ ERR_FAIL_V_MSG(HAND_MOTION_RANGE_MAX, "Unknown motion range returned by OpenXR");
+ }
+ }
+
+ return HAND_MOTION_RANGE_MAX;
+}
+
+Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ return hand_tracking_ext->get_hand_joint_rotation(uint32_t(p_hand), XrHandJointEXT(p_joint));
+ }
+
+ return Quaternion();
+}
+
+Vector3 OpenXRInterface::get_hand_joint_position(Hand p_hand, HandJoints p_joint) const {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ return hand_tracking_ext->get_hand_joint_position(uint32_t(p_hand), XrHandJointEXT(p_joint));
+ }
+
+ return Vector3();
+}
+
+float OpenXRInterface::get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ return hand_tracking_ext->get_hand_joint_radius(uint32_t(p_hand), XrHandJointEXT(p_joint));
+ }
+
+ return 0.0;
+}
+
+Vector3 OpenXRInterface::get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ return hand_tracking_ext->get_hand_joint_linear_velocity(uint32_t(p_hand), XrHandJointEXT(p_joint));
+ }
+
+ return Vector3();
+}
+
+Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const {
+ OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
+ if (hand_tracking_ext && hand_tracking_ext->get_active()) {
+ return hand_tracking_ext->get_hand_joint_angular_velocity(uint32_t(p_hand), XrHandJointEXT(p_joint));
+ }
+
+ return Vector3();
+}
+
OpenXRInterface::OpenXRInterface() {
openxr_api = OpenXRAPI::get_singleton();
if (openxr_api) {
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index 39cc68ae9b5..09e1c31728c 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -161,8 +161,65 @@ public:
void on_pose_recentered();
void tracker_profile_changed(RID p_tracker, RID p_interaction_profile);
+ /** Hand tracking. */
+ enum Hand {
+ HAND_LEFT,
+ HAND_RIGHT,
+ HAND_MAX,
+ };
+
+ enum HandMotionRange {
+ HAND_MOTION_RANGE_UNOBSTRUCTED,
+ HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER,
+ HAND_MOTION_RANGE_MAX
+ };
+
+ void set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range);
+ HandMotionRange get_motion_range(const Hand p_hand) const;
+
+ enum HandJoints {
+ HAND_JOINT_PALM = 0,
+ HAND_JOINT_WRIST = 1,
+ HAND_JOINT_THUMB_METACARPAL = 2,
+ HAND_JOINT_THUMB_PROXIMAL = 3,
+ HAND_JOINT_THUMB_DISTAL = 4,
+ HAND_JOINT_THUMB_TIP = 5,
+ HAND_JOINT_INDEX_METACARPAL = 6,
+ HAND_JOINT_INDEX_PROXIMAL = 7,
+ HAND_JOINT_INDEX_INTERMEDIATE = 8,
+ HAND_JOINT_INDEX_DISTAL = 9,
+ HAND_JOINT_INDEX_TIP = 10,
+ HAND_JOINT_MIDDLE_METACARPAL = 11,
+ HAND_JOINT_MIDDLE_PROXIMAL = 12,
+ HAND_JOINT_MIDDLE_INTERMEDIATE = 13,
+ HAND_JOINT_MIDDLE_DISTAL = 14,
+ HAND_JOINT_MIDDLE_TIP = 15,
+ HAND_JOINT_RING_METACARPAL = 16,
+ HAND_JOINT_RING_PROXIMAL = 17,
+ HAND_JOINT_RING_INTERMEDIATE = 18,
+ HAND_JOINT_RING_DISTAL = 19,
+ HAND_JOINT_RING_TIP = 20,
+ HAND_JOINT_LITTLE_METACARPAL = 21,
+ HAND_JOINT_LITTLE_PROXIMAL = 22,
+ HAND_JOINT_LITTLE_INTERMEDIATE = 23,
+ HAND_JOINT_LITTLE_DISTAL = 24,
+ HAND_JOINT_LITTLE_TIP = 25,
+ HAND_JOINT_MAX = 26,
+ };
+
+ Quaternion get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const;
+ Vector3 get_hand_joint_position(Hand p_hand, HandJoints p_joint) const;
+ float get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const;
+
+ Vector3 get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const;
+ Vector3 get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const;
+
OpenXRInterface();
~OpenXRInterface();
};
+VARIANT_ENUM_CAST(OpenXRInterface::Hand)
+VARIANT_ENUM_CAST(OpenXRInterface::HandMotionRange)
+VARIANT_ENUM_CAST(OpenXRInterface::HandJoints)
+
#endif // OPENXR_INTERFACE_H
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
index 7bec3959d83..edfb474ac76 100644
--- a/modules/openxr/scene/openxr_hand.h
+++ b/modules/openxr/scene/openxr_hand.h
@@ -43,13 +43,13 @@ class OpenXRHand : public Node3D {
GDCLASS(OpenXRHand, Node3D);
public:
- enum Hands {
+ enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands.
HAND_LEFT,
HAND_RIGHT,
HAND_MAX
};
- enum MotionRange {
+ enum MotionRange { // Deprecated, need to change this to OpenXRInterface::HandMotionRange.
MOTION_RANGE_UNOBSTRUCTED,
MOTION_RANGE_CONFORM_TO_CONTROLLER,
MOTION_RANGE_MAX