summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/XRPositionalTracker.xml9
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml27
-rw-r--r--modules/openxr/openxr_api.cpp456
-rw-r--r--modules/openxr/openxr_api.h83
-rw-r--r--modules/openxr/openxr_interface.cpp223
-rw-r--r--modules/openxr/openxr_interface.h48
-rw-r--r--modules/openxr/openxr_util.cpp14
-rw-r--r--modules/openxr/openxr_util.h1
-rw-r--r--modules/openxr/register_types.cpp13
-rw-r--r--servers/xr/xr_positional_tracker.cpp17
-rw-r--r--servers/xr/xr_positional_tracker.h5
11 files changed, 698 insertions, 198 deletions
diff --git a/doc/classes/XRPositionalTracker.xml b/doc/classes/XRPositionalTracker.xml
index da378759d8..d15558c9e8 100644
--- a/doc/classes/XRPositionalTracker.xml
+++ b/doc/classes/XRPositionalTracker.xml
@@ -72,6 +72,9 @@
- [code]left_hand[/code] identifies the controller held in the players left hand
- [code]right_hand[/code] identifies the controller held in the players right hand
</member>
+ <member name="profile" type="String" setter="set_tracker_profile" getter="get_tracker_profile" default="&quot;&quot;">
+ The profile associated with this tracker, interface dependent but will indicate the type of controller being tracked.
+ </member>
<member name="type" type="int" setter="set_tracker_type" getter="get_tracker_type" enum="XRServer.TrackerType" default="128">
The type of tracker.
</member>
@@ -109,6 +112,12 @@
Emitted when the state of a pose tracked by this tracker changes.
</description>
</signal>
+ <signal name="profile_changed">
+ <argument index="0" name="role" type="String" />
+ <description>
+ Emitted when the profile of our tracker changes.
+ </description>
+ </signal>
</signals>
<constants>
<constant name="TRACKER_HAND_UNKNOWN" value="0" enum="TrackerHand">
diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml
index 1160061e04..74f708bc95 100644
--- a/modules/openxr/doc_classes/OpenXRInterface.xml
+++ b/modules/openxr/doc_classes/OpenXRInterface.xml
@@ -10,4 +10,31 @@
<tutorials>
<link title="OpenXR documentation">$DOCS_URL/tutorials/vr/openxr/index.html</link>
</tutorials>
+ <signals>
+ <signal name="pose_recentered">
+ <description>
+ Informs the user queued a recenter of the player position.
+ </description>
+ </signal>
+ <signal name="session_begun">
+ <description>
+ Informs our OpenXR session has been started.
+ </description>
+ </signal>
+ <signal name="session_focussed">
+ <description>
+ Informs our OpenXR session now has focus.
+ </description>
+ </signal>
+ <signal name="session_stopping">
+ <description>
+ Informs our OpenXR session is stopping.
+ </description>
+ </signal>
+ <signal name="session_visible">
+ <description>
+ Informs our OpenXR session is now visible (output is being sent to the HMD).
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index e3da214cc8..4d533337f3 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -48,6 +48,8 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
+#include "modules/openxr/openxr_interface.h"
+
OpenXRAPI *OpenXRAPI::singleton = nullptr;
void OpenXRAPI::setup_global_defs() {
@@ -877,7 +879,9 @@ bool OpenXRAPI::on_state_ready() {
wrapper->on_state_ready();
}
- // TODO emit signal
+ if (xr_interface) {
+ xr_interface->on_state_ready();
+ }
// TODO Tell android
@@ -889,6 +893,13 @@ bool OpenXRAPI::on_state_synchronized() {
print_line("On state synchronized");
#endif
+ // Just in case, see if we already have active trackers...
+ List<RID> trackers;
+ tracker_owner.get_owned_list(&trackers);
+ for (int i = 0; i < trackers.size(); i++) {
+ tracker_check_profile(trackers[i]);
+ }
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_synchronized();
}
@@ -905,7 +916,9 @@ bool OpenXRAPI::on_state_visible() {
wrapper->on_state_visible();
}
- // TODO emit signal
+ if (xr_interface) {
+ xr_interface->on_state_visible();
+ }
return true;
}
@@ -919,7 +932,9 @@ bool OpenXRAPI::on_state_focused() {
wrapper->on_state_focused();
}
- // TODO emit signal
+ if (xr_interface) {
+ xr_interface->on_state_focused();
+ }
return true;
}
@@ -929,7 +944,9 @@ bool OpenXRAPI::on_state_stopping() {
print_line("On state stopping");
#endif
- // TODO emit signal
+ if (xr_interface) {
+ xr_interface->on_state_stopping();
+ }
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_state_stopping();
@@ -1081,6 +1098,10 @@ void OpenXRAPI::finish() {
destroy_instance();
}
+void OpenXRAPI::set_xr_interface(OpenXRInterface *p_xr_interface) {
+ xr_interface = p_xr_interface;
+}
+
void OpenXRAPI::register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper) {
registered_extension_wrappers.push_back(p_extension_wrapper);
}
@@ -1204,20 +1225,38 @@ bool OpenXRAPI::poll_events() {
handled |= wrapper->on_event_polled(runtimeEvent);
}
switch (runtimeEvent.type) {
- // case XR_TYPE_EVENT_DATA_EVENTS_LOST: {
- // } break;
- // case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR: {
- // } break;
- // case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
- // } break;
+ case XR_TYPE_EVENT_DATA_EVENTS_LOST: {
+ XrEventDataEventsLost *event = (XrEventDataEventsLost *)&runtimeEvent;
+
+ // We probably didn't poll fast enough, just output warning
+ WARN_PRINT("OpenXR EVENT: " + itos(event->lostEventCount) + " event data lost!");
+ } break;
+ case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR: {
+ // XrEventDataVisibilityMaskChangedKHR *event = (XrEventDataVisibilityMaskChangedKHR *)&runtimeEvent;
+
+ // TODO implement this in the future, we should call xrGetVisibilityMaskKHR to obtain a mask,
+ // this will allow us to prevent rendering the part of our view which is never displayed giving us
+ // a decent performance improvement.
+
+ print_verbose("OpenXR EVENT: STUB: visibility mask changed");
+ } break;
+ case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
+ XrEventDataInstanceLossPending *event = (XrEventDataInstanceLossPending *)&runtimeEvent;
+
+ // TODO We get this event if we're about to loose our OpenXR instance.
+ // We should queue exiting Godot at this point.
+
+ print_verbose("OpenXR EVENT: instance loss pending at " + itos(event->lossTime));
+ return false;
+ } break;
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
XrEventDataSessionStateChanged *event = (XrEventDataSessionStateChanged *)&runtimeEvent;
session_state = event->state;
if (session_state >= XR_SESSION_STATE_MAX_ENUM) {
- print_line("OpenXR EVENT: session state changed to UNKNOWN -", session_state);
+ print_verbose("OpenXR EVENT: session state changed to UNKNOWN - " + itos(session_state));
} else {
- print_line("OpenXR EVENT: session state changed to", OpenXRUtil::get_session_state_name(session_state));
+ print_verbose("OpenXR EVENT: session state changed to " + OpenXRUtil::get_session_state_name(session_state));
switch (session_state) {
case XR_SESSION_STATE_IDLE:
@@ -1249,13 +1288,29 @@ bool OpenXRAPI::poll_events() {
}
}
} break;
- // case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
- // } break;
- // case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: {
- // } break;
+ case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
+ XrEventDataReferenceSpaceChangePending *event = (XrEventDataReferenceSpaceChangePending *)&runtimeEvent;
+
+ print_verbose("OpenXR EVENT: reference space type " + OpenXRUtil::get_reference_space_name(event->referenceSpaceType) + " change pending!");
+ if (event->poseValid && xr_interface) {
+ xr_interface->on_pose_recentered();
+ }
+ } break;
+ case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: {
+ print_verbose("OpenXR EVENT: interaction profile changed!");
+
+ XrEventDataInteractionProfileChanged *event = (XrEventDataInteractionProfileChanged *)&runtimeEvent;
+
+ List<RID> trackers;
+ tracker_owner.get_owned_list(&trackers);
+ for (int i = 0; i < trackers.size(); i++) {
+ tracker_check_profile(trackers[i], event->session);
+ }
+
+ } break;
default:
if (!handled) {
- print_line("OpenXR Unhandled event type", OpenXRUtil::get_structure_type_name(runtimeEvent.type));
+ print_verbose("OpenXR Unhandled event type " + OpenXRUtil::get_structure_type_name(runtimeEvent.type));
}
break;
}
@@ -1348,9 +1403,21 @@ void OpenXRAPI::pre_render() {
XrResult result = xrWaitFrame(session, &frame_wait_info, &frame_state);
if (XR_FAILED(result)) {
print_line("OpenXR: xrWaitFrame() was not successful [", get_error_string(result), "]");
+
+ // reset just in case
+ frame_state.predictedDisplayTime = 0;
+ frame_state.predictedDisplayPeriod = 0;
+ frame_state.shouldRender = false;
+
return;
}
+ if (frame_state.predictedDisplayPeriod > 500000000) {
+ // display period more then 0.5 seconds? must be wrong data
+ print_verbose("OpenXR resetting invalid display period " + rtos(frame_state.predictedDisplayPeriod));
+ frame_state.predictedDisplayPeriod = 0;
+ }
+
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
wrapper->on_pre_render();
}
@@ -1691,38 +1758,97 @@ void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_l
}
}
-RID OpenXRAPI::path_create(const String p_name) {
- ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
+RID OpenXRAPI::get_tracker_rid(XrPath p_path) {
+ List<RID> current;
+ tracker_owner.get_owned_list(&current);
+ for (int i = 0; i < current.size(); i++) {
+ Tracker *tracker = tracker_owner.get_or_null(current[i]);
+ if (tracker && tracker->toplevel_path == p_path) {
+ return current[i];
+ }
+ }
- // Encoding our path as a RID is probably overkill but it does future proof this
- // Note that we only do this for XrPaths that we access from outside of this class!
+ return RID();
+}
- Path new_path;
+RID OpenXRAPI::tracker_create(const String p_name) {
+ ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
- print_line("Parsing path ", p_name);
+ Tracker new_tracker;
+ new_tracker.name = p_name;
+ new_tracker.toplevel_path = XR_NULL_PATH;
+ new_tracker.active_profile_rid = RID();
- XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_path.path);
+ XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_tracker.toplevel_path);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to get path for ", p_name, "! [", get_error_string(result), "]");
return RID();
}
- return xr_path_owner.make_rid(new_path);
+ return tracker_owner.make_rid(new_tracker);
}
-void OpenXRAPI::path_free(RID p_path) {
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL(path);
+String OpenXRAPI::tracker_get_name(RID p_tracker) {
+ if (p_tracker.is_null()) {
+ return String("None");
+ }
+
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, String());
+
+ return tracker->name;
+}
+
+void OpenXRAPI::tracker_check_profile(RID p_tracker, XrSession p_session) {
+ if (p_session == XR_NULL_HANDLE) {
+ p_session = session;
+ }
+
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL(tracker);
+
+ if (tracker->toplevel_path == XR_NULL_PATH) {
+ // no path, how was this even created?
+ return;
+ }
+
+ XrInteractionProfileState profile_state = {
+ XR_TYPE_INTERACTION_PROFILE_STATE, // type
+ nullptr, // next
+ XR_NULL_PATH // interactionProfile
+ };
+
+ XrResult result = xrGetCurrentInteractionProfile(p_session, tracker->toplevel_path, &profile_state);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: Failed to get interaction profile for", itos(tracker->toplevel_path), "[", get_error_string(result), "]");
+ return;
+ }
+
+ XrPath new_profile = profile_state.interactionProfile;
+ XrPath was_profile = get_interaction_profile_path(tracker->active_profile_rid);
+ if (was_profile != new_profile) {
+ tracker->active_profile_rid = get_interaction_profile_rid(new_profile);
+
+ if (xr_interface) {
+ xr_interface->tracker_profile_changed(p_tracker, tracker->active_profile_rid);
+ }
+ }
+}
+
+void OpenXRAPI::tracker_free(RID p_tracker) {
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL(tracker);
// there is nothing to free here
- xr_path_owner.free(p_path);
+ tracker_owner.free(p_tracker);
}
RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_name, const int p_priority) {
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
ActionSet action_set;
+ action_set.name = p_name;
action_set.is_attached = false;
// create our action set...
@@ -1737,7 +1863,7 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
copy_string_to_char_buffer(p_name, action_set_info.actionSetName, XR_MAX_ACTION_SET_NAME_SIZE);
copy_string_to_char_buffer(p_localized_name, action_set_info.localizedActionSetName, XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE);
- print_line("Creating action set ", action_set_info.actionSetName, " - ", action_set_info.localizedActionSetName, " (", itos(action_set_info.priority), ")");
+ // print_line("Creating action set ", action_set_info.actionSetName, " - ", action_set_info.localizedActionSetName, " (", itos(action_set_info.priority), ")");
XrResult result = xrCreateActionSet(instance, &action_set_info, &action_set.handle);
if (XR_FAILED(result)) {
@@ -1748,6 +1874,17 @@ RID OpenXRAPI::action_set_create(const String p_name, const String p_localized_n
return action_set_owner.make_rid(action_set);
}
+String OpenXRAPI::action_set_get_name(RID p_action_set) {
+ if (p_action_set.is_null()) {
+ return String("None");
+ }
+
+ ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
+ ERR_FAIL_NULL_V(action_set, String());
+
+ return action_set->name;
+}
+
bool OpenXRAPI::action_set_attach(RID p_action_set) {
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
ERR_FAIL_NULL_V(action_set, false);
@@ -1776,6 +1913,24 @@ bool OpenXRAPI::action_set_attach(RID p_action_set) {
action_set->is_attached = true;
+ /* For debugging:
+ print_verbose("Attached set " + action_set->name);
+ List<RID> action_rids;
+ action_owner.get_owned_list(&action_rids);
+ for (int i = 0; i < action_rids.size(); i++) {
+ Action * action = action_owner.get_or_null(action_rids[i]);
+ if (action && action->action_set_rid == p_action_set) {
+ print_verbose(" - Action " + action->name + ": " + OpenXRUtil::get_action_type_name(action->action_type));
+ for (int j = 0; j < action->trackers.size(); j++) {
+ Tracker * tracker = tracker_owner.get_or_null(action->trackers[j].tracker_rid);
+ if (tracker) {
+ print_verbose(" - " + tracker->name);
+ }
+ }
+ }
+ }
+ */
+
return true;
}
@@ -1790,14 +1945,29 @@ void OpenXRAPI::action_set_free(RID p_action_set) {
action_set_owner.free(p_action_set);
}
-RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_toplevel_paths) {
+RID OpenXRAPI::get_action_rid(XrAction p_action) {
+ List<RID> current;
+ action_owner.get_owned_list(&current);
+ for (int i = 0; i < current.size(); i++) {
+ Action *action = action_owner.get_or_null(current[i]);
+ if (action && action->handle == p_action) {
+ return current[i];
+ }
+ }
+
+ return RID();
+}
+
+RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_trackers) {
ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, RID());
Action action;
+ action.name = p_name;
ActionSet *action_set = action_set_owner.get_or_null(p_action_set);
ERR_FAIL_NULL_V(action_set, RID());
ERR_FAIL_COND_V(action_set->handle == XR_NULL_HANDLE, RID());
+ action.action_set_rid = p_action_set;
switch (p_action_type) {
case OpenXRAction::OPENXR_ACTION_BOOL:
@@ -1821,17 +1991,17 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
}
Vector<XrPath> toplevel_paths;
- for (int i = 0; i < p_toplevel_paths.size(); i++) {
- Path *xr_path = xr_path_owner.get_or_null(p_toplevel_paths[i]);
- if (xr_path != nullptr && xr_path->path != XR_NULL_PATH) {
- PathWithSpace path_with_space = {
- xr_path->path, // toplevel_path
+ for (int i = 0; i < p_trackers.size(); i++) {
+ Tracker *tracker = tracker_owner.get_or_null(p_trackers[i]);
+ if (tracker != nullptr && tracker->toplevel_path != XR_NULL_PATH) {
+ ActionTracker action_tracker = {
+ p_trackers[i], // tracker
XR_NULL_HANDLE, // space
false // was_location_valid
};
- action.toplevel_paths.push_back(path_with_space);
+ action.trackers.push_back(action_tracker);
- toplevel_paths.push_back(xr_path->path);
+ toplevel_paths.push_back(tracker->toplevel_path);
}
}
@@ -1848,7 +2018,7 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
copy_string_to_char_buffer(p_name, action_info.actionName, XR_MAX_ACTION_NAME_SIZE);
copy_string_to_char_buffer(p_localized_name, action_info.localizedActionName, XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
- print_line("Creating action ", action_info.actionName, action_info.localizedActionName, action_info.countSubactionPaths);
+ // print_line("Creating action ", action_info.actionName, action_info.localizedActionName, action_info.countSubactionPaths);
XrResult result = xrCreateAction(action_set->handle, &action_info, &action.handle);
if (XR_FAILED(result)) {
@@ -1859,6 +2029,17 @@ RID OpenXRAPI::action_create(RID p_action_set, const String p_name, const String
return action_owner.make_rid(action);
}
+String OpenXRAPI::action_get_name(RID p_action) {
+ if (p_action.is_null()) {
+ return String("None");
+ }
+
+ Action *action = action_owner.get_or_null(p_action);
+ ERR_FAIL_NULL_V(action, String());
+
+ return action->name;
+}
+
void OpenXRAPI::action_free(RID p_action) {
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL(action);
@@ -1870,55 +2051,139 @@ void OpenXRAPI::action_free(RID p_action) {
action_owner.free(p_action);
}
-bool OpenXRAPI::suggest_bindings(const String p_interaction_profile, const Vector<Binding> p_bindings) {
- ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+RID OpenXRAPI::get_interaction_profile_rid(XrPath p_path) {
+ List<RID> current;
+ interaction_profile_owner.get_owned_list(&current);
+ for (int i = 0; i < current.size(); i++) {
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(current[i]);
+ if (ip && ip->path == p_path) {
+ return current[i];
+ }
+ }
+
+ return RID();
+}
+
+XrPath OpenXRAPI::get_interaction_profile_path(RID p_interaction_profile) {
+ if (p_interaction_profile.is_null()) {
+ return XR_NULL_PATH;
+ }
+
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL_V(ip, XR_NULL_PATH);
- XrPath interaction_profile;
- Vector<XrActionSuggestedBinding> bindings;
+ return ip->path;
+}
- XrResult result = xrStringToPath(instance, p_interaction_profile.utf8().get_data(), &interaction_profile);
+RID OpenXRAPI::interaction_profile_create(const String p_name) {
+ InteractionProfile new_interaction_profile;
+
+ XrResult result = xrStringToPath(instance, p_name.utf8().get_data(), &new_interaction_profile.path);
if (XR_FAILED(result)) {
- print_line("OpenXR: failed to get path for ", p_interaction_profile, "! [", get_error_string(result), "]");
- return false;
+ print_line("OpenXR: failed to get path for ", p_name, "! [", get_error_string(result), "]");
+ return RID();
}
- for (int i = 0; i < p_bindings.size(); i++) {
- XrActionSuggestedBinding binding;
+ RID existing_ip = get_interaction_profile_rid(new_interaction_profile.path);
+ if (existing_ip.is_valid()) {
+ return existing_ip;
+ }
- Action *action = action_owner.get_or_null(p_bindings[i].action);
- if (action == nullptr || action->handle == XR_NULL_HANDLE) {
- // just skip it
- continue;
- }
+ new_interaction_profile.name = p_name;
+ return interaction_profile_owner.make_rid(new_interaction_profile);
+}
- binding.action = action->handle;
+String OpenXRAPI::interaction_profile_get_name(RID p_interaction_profile) {
+ if (p_interaction_profile.is_null()) {
+ return String("None");
+ }
- result = xrStringToPath(instance, p_bindings[i].path.utf8().get_data(), &binding.binding);
- if (XR_FAILED(result)) {
- print_line("OpenXR: failed to get path for ", p_bindings[i].path, "! [", get_error_string(result), "]");
- continue;
- }
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL_V(ip, String());
+
+ return ip->name;
+}
+
+void OpenXRAPI::interaction_profile_clear_bindings(RID p_interaction_profile) {
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL(ip);
+
+ ip->bindings.clear();
+}
+
+bool OpenXRAPI::interaction_profile_add_binding(RID p_interaction_profile, RID p_action, const String p_path) {
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL_V(ip, false);
+
+ XrActionSuggestedBinding binding;
+
+ Action *action = action_owner.get_or_null(p_action);
+ ERR_FAIL_COND_V(action == nullptr || action->handle == XR_NULL_HANDLE, false);
- bindings.push_back(binding);
+ binding.action = action->handle;
+
+ XrResult result = xrStringToPath(instance, p_path.utf8().get_data(), &binding.binding);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: failed to get path for ", p_path, "! [", get_error_string(result), "]");
+ return false;
}
+ ip->bindings.push_back(binding);
+
+ return true;
+}
+
+bool OpenXRAPI::interaction_profile_suggest_bindings(RID p_interaction_profile) {
+ ERR_FAIL_COND_V(instance == XR_NULL_HANDLE, false);
+
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL_V(ip, false);
+
const XrInteractionProfileSuggestedBinding suggested_bindings = {
XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING, // type
nullptr, // next
- interaction_profile, // interactionProfile
- uint32_t(bindings.size()), // countSuggestedBindings
- bindings.ptr() // suggestedBindings
+ ip->path, // interactionProfile
+ uint32_t(ip->bindings.size()), // countSuggestedBindings
+ ip->bindings.ptr() // suggestedBindings
};
- result = xrSuggestInteractionProfileBindings(instance, &suggested_bindings);
- if (XR_FAILED(result)) {
- print_line("OpenXR: failed to suggest bindings for ", p_interaction_profile, "! [", get_error_string(result), "]");
+ XrResult result = xrSuggestInteractionProfileBindings(instance, &suggested_bindings);
+ if (result == XR_ERROR_PATH_UNSUPPORTED) {
+ // this is fine, not all runtimes support all devices.
+ print_verbose("OpenXR Interaction profile " + ip->name + " is not supported on this runtime");
+ } else if (XR_FAILED(result)) {
+ print_line("OpenXR: failed to suggest bindings for ", ip->name, "! [", get_error_string(result), "]");
// reporting is enough...
}
+ /* For debugging:
+ print_verbose("Suggested bindings for " + ip->name);
+ for (int i = 0; i < ip->bindings.size(); i++) {
+ uint32_t strlen;
+ char path[XR_MAX_PATH_LENGTH];
+
+ String action_name = action_get_name(get_action_rid(ip->bindings[i].action));
+
+ XrResult result = xrPathToString(instance, ip->bindings[i].binding, XR_MAX_PATH_LENGTH, &strlen, path);
+ if (XR_FAILED(result)) {
+ print_line("OpenXR: failed to retrieve bindings for ", action_name, "! [", get_error_string(result), "]");
+ }
+ print_verbose(" - " + action_name + " => " + String(path));
+ }
+ */
+
return true;
}
+void OpenXRAPI::interaction_profile_free(RID p_interaction_profile) {
+ InteractionProfile *ip = interaction_profile_owner.get_or_null(p_interaction_profile);
+ ERR_FAIL_NULL(ip);
+
+ ip->bindings.clear();
+
+ interaction_profile_owner.free(p_interaction_profile);
+}
+
bool OpenXRAPI::sync_action_sets(const Vector<RID> p_active_sets) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
@@ -1955,12 +2220,12 @@ bool OpenXRAPI::sync_action_sets(const Vector<RID> p_active_sets) {
return true;
}
-bool OpenXRAPI::get_action_bool(RID p_action, RID p_path) {
+bool OpenXRAPI::get_action_bool(RID p_action, RID p_tracker) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, false);
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL_V(path, false);
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, false);
if (!running) {
return false;
@@ -1972,7 +2237,7 @@ bool OpenXRAPI::get_action_bool(RID p_action, RID p_path) {
XR_TYPE_ACTION_STATE_GET_INFO, // type
nullptr, // next
action->handle, // action
- path->path // subactionPath
+ tracker->toplevel_path // subactionPath
};
XrActionStateBoolean result_state;
@@ -1987,12 +2252,12 @@ bool OpenXRAPI::get_action_bool(RID p_action, RID p_path) {
return result_state.isActive && result_state.currentState;
}
-float OpenXRAPI::get_action_float(RID p_action, RID p_path) {
+float OpenXRAPI::get_action_float(RID p_action, RID p_tracker) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, 0.0);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, 0.0);
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL_V(path, 0.0);
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, 0.0);
if (!running) {
return 0.0;
@@ -2004,7 +2269,7 @@ float OpenXRAPI::get_action_float(RID p_action, RID p_path) {
XR_TYPE_ACTION_STATE_GET_INFO, // type
nullptr, // next
action->handle, // action
- path->path // subactionPath
+ tracker->toplevel_path // subactionPath
};
XrActionStateFloat result_state;
@@ -2019,12 +2284,12 @@ float OpenXRAPI::get_action_float(RID p_action, RID p_path) {
return result_state.isActive ? result_state.currentState : 0.0;
}
-Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_path) {
+Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_tracker) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, Vector2());
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, Vector2());
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL_V(path, Vector2());
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, Vector2());
if (!running) {
return Vector2();
@@ -2036,7 +2301,7 @@ Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_path) {
XR_TYPE_ACTION_STATE_GET_INFO, // type
nullptr, // next
action->handle, // action
- path->path // subactionPath
+ tracker->toplevel_path // subactionPath
};
XrActionStateVector2f result_state;
@@ -2051,12 +2316,12 @@ Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_path) {
return result_state.isActive ? Vector2(result_state.currentState.x, result_state.currentState.y) : Vector2();
}
-XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, XRPose::XR_TRACKING_CONFIDENCE_NONE);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, XRPose::XR_TRACKING_CONFIDENCE_NONE);
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL_V(path, XRPose::XR_TRACKING_CONFIDENCE_NONE);
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, XRPose::XR_TRACKING_CONFIDENCE_NONE);
if (!running) {
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
@@ -2064,10 +2329,12 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path,
ERR_FAIL_COND_V(action->action_type != XR_ACTION_TYPE_POSE_INPUT, XRPose::XR_TRACKING_CONFIDENCE_NONE);
+ // print_verbose("Checking " + action->name + " => " + tracker->name + " (" + itos(tracker->toplevel_path) + ")");
+
uint64_t index = 0xFFFFFFFF;
- uint64_t size = uint64_t(action->toplevel_paths.size());
+ uint64_t size = uint64_t(action->trackers.size());
for (uint64_t i = 0; i < size && index == 0xFFFFFFFF; i++) {
- if (action->toplevel_paths[i].toplevel_path == path->path) {
+ if (action->trackers[i].tracker_rid == p_tracker) {
index = i;
}
}
@@ -2077,14 +2344,19 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path,
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
- if (action->toplevel_paths[index].space == XR_NULL_HANDLE) {
+ XrTime display_time = get_next_frame_time();
+ if (display_time == 0) {
+ return XRPose::XR_TRACKING_CONFIDENCE_NONE;
+ }
+
+ if (action->trackers[index].space == XR_NULL_HANDLE) {
// if this is a pose we need to define spaces
XrActionSpaceCreateInfo action_space_info = {
XR_TYPE_ACTION_SPACE_CREATE_INFO, // type
nullptr, // next
action->handle, // action
- action->toplevel_paths[index].toplevel_path, // subactionPath
+ tracker->toplevel_path, // subactionPath
{
{ 0.0, 0.0, 0.0, 1.0 }, // orientation
{ 0.0, 0.0, 0.0 } // position
@@ -2098,11 +2370,9 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path,
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
}
- action->toplevel_paths.ptrw()[index].space = space;
+ action->trackers.ptrw()[index].space = space;
}
- XrTime display_time = get_next_frame_time();
-
XrSpaceVelocity velocity = {
XR_TYPE_SPACE_VELOCITY, // type
nullptr, // next
@@ -2121,7 +2391,7 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path,
} // pose
};
- XrResult result = xrLocateSpace(action->toplevel_paths[index].space, play_space, display_time, &location);
+ XrResult result = xrLocateSpace(action->trackers[index].space, play_space, display_time, &location);
if (XR_FAILED(result)) {
print_line("OpenXR: failed to locate space! [", get_error_string(result), "]");
return XRPose::XR_TRACKING_CONFIDENCE_NONE;
@@ -2133,12 +2403,12 @@ XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_path,
return confidence;
}
-bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_path, float p_frequency, float p_amplitude, XrDuration p_duration_ns) {
+bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, false);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, false);
- Path *path = xr_path_owner.get_or_null(p_path);
- ERR_FAIL_NULL_V(path, false);
+ Tracker *tracker = tracker_owner.get_or_null(p_tracker);
+ ERR_FAIL_NULL_V(tracker, false);
if (!running) {
return false;
@@ -2150,7 +2420,7 @@ bool OpenXRAPI::trigger_haptic_pulse(RID p_action, RID p_path, float p_frequency
XR_TYPE_HAPTIC_ACTION_INFO, // type
nullptr, // next
action->handle, // action
- path->path // subactionPath
+ tracker->toplevel_path // subactionPath
};
XrHapticVibration vibration = {
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 33b503543a..e20826c849 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -55,12 +55,16 @@
// forward declarations, we don't want to include these fully
class OpenXRVulkanExtension;
+class OpenXRInterface;
class OpenXRAPI {
private:
// our singleton
static OpenXRAPI *singleton;
+ // linked XR interface
+ OpenXRInterface *xr_interface = nullptr;
+
// layers
uint32_t num_layer_properties = 0;
XrApiLayerProperties *layer_properties = nullptr;
@@ -148,29 +152,45 @@ private:
bool release_image(XrSwapchain p_swapchain);
// action map
- struct Path {
- XrPath path;
+ struct Tracker { // Trackers represent tracked physical objects such as controllers, pucks, etc.
+ String name; // Name for this tracker (i.e. "/user/hand/left")
+ XrPath toplevel_path; // OpenXR XrPath for this tracker
+ RID active_profile_rid; // RID of the active profile for this tracker
};
- RID_Owner<Path, true> xr_path_owner;
+ RID_Owner<Tracker, true> tracker_owner;
+ RID get_tracker_rid(XrPath p_path);
- struct ActionSet {
- bool is_attached;
- XrActionSet handle;
+ struct ActionSet { // Action sets define a set of actions that can be enabled together
+ String name; // Name for this action set (i.e. "godot_action_set")
+ bool is_attached; // If true our action set has been attached to the session and can no longer be modified
+ XrActionSet handle; // OpenXR handle for this action set
};
RID_Owner<ActionSet, true> action_set_owner;
- struct PathWithSpace {
- XrPath toplevel_path;
- XrSpace space;
- bool was_location_valid;
+ struct ActionTracker { // Links and action to a tracker
+ RID tracker_rid; // RID of the tracker
+ XrSpace space; // Optional space for pose actions
+ bool was_location_valid; // If true the last position we obtained was valid
};
- struct Action {
- XrActionType action_type;
- Vector<PathWithSpace> toplevel_paths;
- XrAction handle;
+ struct Action { // Actions define the inputs and outputs in OpenXR
+ RID action_set_rid; // RID of the action set this action belongs to
+ String name; // Name for this action (i.e. "aim_pose")
+ XrActionType action_type; // Type of action (bool, float, etc.)
+ Vector<ActionTracker> trackers; // The trackers this action can be used with
+ XrAction handle; // OpenXR handle for this action
};
RID_Owner<Action, true> action_owner;
+ RID get_action_rid(XrAction p_action);
+
+ struct InteractionProfile { // Interaction profiles define suggested bindings between the physical inputs on controller types and our actions
+ String name; // Name of the interaction profile (i.e. "/interaction_profiles/valve/index_controller")
+ XrPath path; // OpenXR path for this profile
+ Vector<XrActionSuggestedBinding> bindings; // OpenXR action bindings
+ };
+ RID_Owner<InteractionProfile, true> interaction_profile_owner;
+ RID get_interaction_profile_rid(XrPath p_path);
+ XrPath get_interaction_profile_path(RID p_interaction_profile);
// state changes
bool poll_events();
@@ -209,6 +229,7 @@ public:
String get_error_string(XrResult result);
String get_swapchain_format_name(int64_t p_swapchain_format) const;
+ void set_xr_interface(OpenXRInterface *p_xr_interface);
void register_extension_wrapper(OpenXRExtensionWrapper *p_extension_wrapper);
bool is_initialized();
@@ -233,26 +254,34 @@ public:
// action map
String get_default_action_map_resource_name();
- RID path_create(const String p_name);
- void path_free(RID p_path);
+
+ RID tracker_create(const String p_name);
+ String tracker_get_name(RID p_tracker);
+ void tracker_check_profile(RID p_tracker, XrSession p_session = XR_NULL_HANDLE);
+ void tracker_free(RID p_tracker);
+
RID action_set_create(const String p_name, const String p_localized_name, const int p_priority);
+ String action_set_get_name(RID p_action_set);
bool action_set_attach(RID p_action_set);
void action_set_free(RID p_action_set);
- RID action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_toplevel_paths);
+
+ RID action_create(RID p_action_set, const String p_name, const String p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> &p_trackers);
+ String action_get_name(RID p_action);
void action_free(RID p_action);
- struct Binding {
- RID action;
- String path;
- };
- bool suggest_bindings(const String p_interaction_profile, const Vector<Binding> p_bindings);
+ RID interaction_profile_create(const String p_name);
+ String interaction_profile_get_name(RID p_interaction_profile);
+ void interaction_profile_clear_bindings(RID p_interaction_profile);
+ bool interaction_profile_add_binding(RID p_interaction_profile, RID p_action, const String p_path);
+ bool interaction_profile_suggest_bindings(RID p_interaction_profile);
+ void interaction_profile_free(RID p_interaction_profile);
bool sync_action_sets(const Vector<RID> p_active_sets);
- bool get_action_bool(RID p_action, RID p_path);
- float get_action_float(RID p_action, RID p_path);
- Vector2 get_action_vector2(RID p_action, RID p_path);
- XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_path, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
- bool trigger_haptic_pulse(RID p_action, RID p_path, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
+ bool get_action_bool(RID p_action, RID p_tracker);
+ float get_action_float(RID p_action, RID p_tracker);
+ Vector2 get_action_vector2(RID p_action, RID p_tracker);
+ XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
+ bool trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
OpenXRAPI();
~OpenXRAPI();
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 394f634687..b89171543f 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -35,7 +35,12 @@
#include "servers/rendering/rendering_server_globals.h"
void OpenXRInterface::_bind_methods() {
- // todo
+ // lifecycle signals
+ ADD_SIGNAL(MethodInfo("session_begun"));
+ ADD_SIGNAL(MethodInfo("session_stopping"));
+ ADD_SIGNAL(MethodInfo("session_focussed"));
+ ADD_SIGNAL(MethodInfo("session_visible"));
+ ADD_SIGNAL(MethodInfo("pose_recentered"));
}
StringName OpenXRInterface::get_name() const {
@@ -46,6 +51,18 @@ uint32_t OpenXRInterface::get_capabilities() const {
return XRInterface::XR_VR + XRInterface::XR_STEREO;
};
+PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {
+ // These are hardcoded in OpenXR, note that they will only be available if added to our action map
+
+ PackedStringArray arr = {
+ "left_hand", // /user/hand/left is mapped to our defaults
+ "right_hand", // /user/hand/right is mapped to our defaults
+ "/user/treadmill"
+ };
+
+ return arr;
+}
+
XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const {
return tracking_state;
}
@@ -64,8 +81,9 @@ void OpenXRInterface::_load_action_map() {
// This allow us to process the relevant actions each frame.
// just in case clean up
- free_action_sets();
free_trackers();
+ free_interaction_profiles();
+ free_action_sets();
Ref<OpenXRActionMap> action_map;
if (Engine::get_singleton()->is_editor_hint()) {
@@ -95,7 +113,7 @@ void OpenXRInterface::_load_action_map() {
// process our action map
if (action_map.is_valid()) {
- Map<Ref<OpenXRAction>, RID> action_rids;
+ Map<Ref<OpenXRAction>, Action *> xr_actions;
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
@@ -112,18 +130,16 @@ void OpenXRInterface::_load_action_map() {
Ref<OpenXRAction> xr_action = actions[j];
PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();
- Vector<RID> toplevel_rids;
Vector<Tracker *> trackers;
for (int k = 0; k < toplevel_paths.size(); k++) {
- Tracker *tracker = get_tracker(toplevel_paths[k]);
+ Tracker *tracker = find_tracker(toplevel_paths[k], true);
if (tracker) {
- toplevel_rids.push_back(tracker->path_rid);
trackers.push_back(tracker);
}
}
- Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), toplevel_rids);
+ Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers);
if (action) {
// we link our actions back to our trackers so we know which actions to check when we're processing our trackers
for (int t = 0; t < trackers.size(); t++) {
@@ -131,7 +147,7 @@ void OpenXRInterface::_load_action_map() {
}
// add this to our map for creating our interaction profiles
- action_rids[xr_action] = action->action_rid;
+ xr_actions[xr_action] = action;
}
}
}
@@ -139,30 +155,38 @@ void OpenXRInterface::_load_action_map() {
// now do our suggestions
Array interaction_profiles = action_map->get_interaction_profiles();
for (int i = 0; i < interaction_profiles.size(); i++) {
- Vector<OpenXRAPI::Binding> bindings;
Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profiles[i];
+ // Note, we can only have one entry per interaction profile so if it already exists we clear it out
+ RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path());
+ openxr_api->interaction_profile_clear_bindings(ip);
+
Array xr_bindings = xr_interaction_profile->get_bindings();
for (int j = 0; j < xr_bindings.size(); j++) {
Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];
Ref<OpenXRAction> xr_action = xr_binding->get_action();
- OpenXRAPI::Binding binding;
- if (action_rids.has(xr_action)) {
- binding.action = action_rids[xr_action];
+ Action *action = nullptr;
+ if (xr_actions.has(xr_action)) {
+ action = xr_actions[xr_action];
} else {
print_line("Action ", xr_action->get_name(), " isn't part of an action set!");
continue;
}
- PackedStringArray xr_paths = xr_binding->get_paths();
- for (int k = 0; k < xr_paths.size(); k++) {
- binding.path = xr_paths[k];
- bindings.push_back(binding);
+ PackedStringArray paths = xr_binding->get_paths();
+ for (int k = 0; k < paths.size(); k++) {
+ openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]);
}
}
- openxr_api->suggest_bindings(xr_interaction_profile->get_interaction_profile_path(), bindings);
+ // Now submit our suggestions
+ openxr_api->interaction_profile_suggest_bindings(ip);
+
+ // And record it in our array so we can clean it up later on
+ if (interaction_profiles.has(ip)) {
+ interaction_profiles.push_back(ip);
+ }
}
}
}
@@ -193,15 +217,16 @@ void OpenXRInterface::free_action_sets() {
for (int i = 0; i < action_sets.size(); i++) {
ActionSet *action_set = action_sets[i];
- openxr_api->path_free(action_set->action_set_rid);
free_actions(action_set);
+ openxr_api->action_set_free(action_set->action_set_rid);
+
memfree(action_set);
}
action_sets.clear();
}
-OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> p_toplevel_paths) {
+OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<Tracker *> p_trackers) {
ERR_FAIL_NULL_V(openxr_api, nullptr);
for (int i = 0; i < p_action_set->actions.size(); i++) {
@@ -211,10 +236,31 @@ OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set,
}
}
+ Vector<RID> tracker_rids;
+ for (int i = 0; i < p_trackers.size(); i++) {
+ tracker_rids.push_back(p_trackers[i]->tracker_rid);
+ }
+
Action *action = memnew(Action);
- action->action_name = p_action_name;
+ if (p_action_type == OpenXRAction::OPENXR_ACTION_POSE) {
+ // We can't have dual action names in OpenXR hence we added _pose,
+ // but default, aim and grip and default pose action names in Godot so rename them on the tracker.
+ // NOTE need to decide on whether we should keep the naming convention or rename it on Godots side
+ if (p_action_name == "default_pose") {
+ action->action_name = "default";
+ } else if (p_action_name == "aim_pose") {
+ action->action_name = "aim";
+ } else if (p_action_name == "grip_pose") {
+ action->action_name = "grip";
+ } else {
+ action->action_name = p_action_name;
+ }
+ } else {
+ action->action_name = p_action_name;
+ }
+
action->action_type = p_action_type;
- action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, p_toplevel_paths);
+ action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids);
p_action_set->actions.push_back(action);
return action;
@@ -248,7 +294,7 @@ void OpenXRInterface::free_actions(ActionSet *p_action_set) {
p_action_set->actions.clear();
}
-OpenXRInterface::Tracker *OpenXRInterface::get_tracker(const String &p_path_name) {
+OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_name, bool p_create) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, nullptr);
ERR_FAIL_NULL_V(openxr_api, nullptr);
@@ -256,52 +302,72 @@ OpenXRInterface::Tracker *OpenXRInterface::get_tracker(const String &p_path_name
Tracker *tracker = nullptr;
for (int i = 0; i < trackers.size(); i++) {
tracker = trackers[i];
- if (tracker->path_name == p_path_name) {
+ if (tracker->tracker_name == p_tracker_name) {
return tracker;
}
}
+ if (!p_create) {
+ return nullptr;
+ }
+
+ // Create our RID
+ RID tracker_rid = openxr_api->tracker_create(p_tracker_name);
+ ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);
+
// create our positional tracker
Ref<XRPositionalTracker> positional_tracker;
positional_tracker.instantiate();
// We have standardised some names to make things nicer to the user so lets recognise the toplevel paths related to these.
- if (p_path_name == "/user/hand/left") {
+ if (p_tracker_name == "/user/hand/left") {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name("left_hand");
positional_tracker->set_tracker_desc("Left hand controller");
positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);
- } else if (p_path_name == "/user/hand/right") {
+ } else if (p_tracker_name == "/user/hand/right") {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
positional_tracker->set_tracker_name("right_hand");
positional_tracker->set_tracker_desc("Right hand controller");
positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);
} else {
positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
- positional_tracker->set_tracker_name(p_path_name);
- positional_tracker->set_tracker_desc(p_path_name);
+ positional_tracker->set_tracker_name(p_tracker_name);
+ positional_tracker->set_tracker_desc(p_tracker_name);
}
+ positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
xr_server->add_tracker(positional_tracker);
// create a new entry
tracker = memnew(Tracker);
- tracker->path_name = p_path_name;
- tracker->path_rid = openxr_api->path_create(p_path_name);
+ tracker->tracker_name = p_tracker_name;
+ tracker->tracker_rid = tracker_rid;
tracker->positional_tracker = positional_tracker;
+ tracker->interaction_profile = RID();
trackers.push_back(tracker);
return tracker;
}
-OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_positional_tracker_name) {
- for (int i = 0; i < trackers.size(); i++) {
- Tracker *tracker = trackers[i];
- if (tracker->positional_tracker.is_valid() && tracker->positional_tracker->get_tracker_name() == p_positional_tracker_name) {
- return tracker;
+void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_profile) {
+ Tracker *tracker = nullptr;
+ for (int i = 0; i < trackers.size() && tracker == nullptr; i++) {
+ if (trackers[i]->tracker_rid == p_tracker) {
+ tracker = trackers[i];
}
}
+ ERR_FAIL_NULL(tracker);
- return nullptr;
+ tracker->interaction_profile = p_interaction_profile;
+
+ if (p_interaction_profile.is_null()) {
+ print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + INTERACTION_PROFILE_NONE);
+ tracker->positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
+ } else {
+ String name = openxr_api->interaction_profile_get_name(p_interaction_profile);
+ print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + name);
+ tracker->positional_tracker->set_tracker_profile(name);
+ }
}
void OpenXRInterface::link_action_to_tracker(Tracker *p_tracker, Action *p_action) {
@@ -314,40 +380,43 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
ERR_FAIL_NULL(openxr_api);
ERR_FAIL_COND(p_tracker->positional_tracker.is_null());
- // handle all the actions
+ // Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction
+ // profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these
+ // and rebind them or even offer bindings to controllers that are not known to us.
+
+ // We don't really have a consistant way to detect whether a controller is active however as long as it is
+ // unbound it seems to be unavailable, so far unknown controller seem to mimic one of the profiles we've
+ // supplied.
+ if (p_tracker->interaction_profile.is_null()) {
+ return;
+ }
+
+ // We check all actions that are related to our tracker.
for (int i = 0; i < p_tracker->actions.size(); i++) {
Action *action = p_tracker->actions[i];
switch (action->action_type) {
case OpenXRAction::OPENXR_ACTION_BOOL: {
- bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->path_rid);
+ bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(pressed));
} break;
case OpenXRAction::OPENXR_ACTION_FLOAT: {
- real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->path_rid);
+ real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
- Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->path_rid);
+ Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid);
p_tracker->positional_tracker->set_input(action->action_name, Variant(value));
} break;
case OpenXRAction::OPENXR_ACTION_POSE: {
Transform3D transform;
Vector3 linear, angular;
- XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->path_rid, transform, linear, angular);
+
+ XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular);
+
if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
- String name;
- // We can't have dual action names in OpenXR hence we added _pose, but default, aim and grip and default pose action names in Godot so rename them on the tracker.
- // NOTE need to decide on whether we should keep the naming convention or rename it on Godots side
- if (action->action_name == "default_pose") {
- name = "default";
- } else if (action->action_name == "aim_pose") {
- name = "aim";
- } else if (action->action_name == "grip_pose") {
- name = "grip";
- } else {
- name = action->action_name;
- }
- p_tracker->positional_tracker->set_pose(name, transform, linear, angular, confidence);
+ p_tracker->positional_tracker->set_pose(action->action_name, transform, linear, angular, confidence);
+ } else {
+ p_tracker->positional_tracker->invalidate_pose(action->action_name);
}
} break;
default: {
@@ -368,7 +437,7 @@ void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const St
XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds
- openxr_api->trigger_haptic_pulse(action->action_rid, tracker->path_rid, p_frequency, p_amplitude, duration);
+ openxr_api->trigger_haptic_pulse(action->action_rid, tracker->tracker_rid, p_frequency, p_amplitude, duration);
}
void OpenXRInterface::free_trackers() {
@@ -379,7 +448,7 @@ void OpenXRInterface::free_trackers() {
for (int i = 0; i < trackers.size(); i++) {
Tracker *tracker = trackers[i];
- openxr_api->path_free(tracker->path_rid);
+ openxr_api->tracker_free(tracker->tracker_rid);
xr_server->remove_tracker(tracker->positional_tracker);
tracker->positional_tracker.unref();
@@ -388,6 +457,15 @@ void OpenXRInterface::free_trackers() {
trackers.clear();
}
+void OpenXRInterface::free_interaction_profiles() {
+ ERR_FAIL_NULL(openxr_api);
+
+ for (int i = 0; i < interaction_profiles.size(); i++) {
+ openxr_api->interaction_profile_free(interaction_profiles[i]);
+ }
+ interaction_profiles.clear();
+}
+
bool OpenXRInterface::initialise_on_startup() const {
if (openxr_api == nullptr) {
return false;
@@ -447,14 +525,14 @@ void OpenXRInterface::uninitialize() {
// end the session if we need to?
// cleanup stuff
- free_action_sets();
free_trackers();
+ free_interaction_profiles();
+ free_action_sets();
XRServer *xr_server = XRServer::get_singleton();
if (xr_server) {
if (head.is_valid()) {
xr_server->remove_tracker(head);
-
head.unref();
}
}
@@ -649,8 +727,31 @@ void OpenXRInterface::end_frame() {
}
}
+void OpenXRInterface::on_state_ready() {
+ emit_signal(SNAME("session_begun"));
+}
+
+void OpenXRInterface::on_state_visible() {
+ emit_signal(SNAME("session_visible"));
+}
+
+void OpenXRInterface::on_state_focused() {
+ emit_signal(SNAME("session_focussed"));
+}
+
+void OpenXRInterface::on_state_stopping() {
+ emit_signal(SNAME("session_stopping"));
+}
+
+void OpenXRInterface::on_pose_recentered() {
+ emit_signal(SNAME("pose_recentered"));
+}
+
OpenXRInterface::OpenXRInterface() {
openxr_api = OpenXRAPI::get_singleton();
+ if (openxr_api) {
+ openxr_api->set_xr_interface(this);
+ }
// while we don't have head tracking, don't put the headset on the floor...
_set_default_pos(head_transform, 1.0, 0);
@@ -659,5 +760,11 @@ OpenXRInterface::OpenXRInterface() {
}
OpenXRInterface::~OpenXRInterface() {
- openxr_api = nullptr;
+ // should already have been called but just in case...
+ uninitialize();
+
+ if (openxr_api) {
+ openxr_api->set_xr_interface(nullptr);
+ openxr_api = nullptr;
+ }
}
diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h
index ede7d481d2..421838e445 100644
--- a/modules/openxr/openxr_interface.h
+++ b/modules/openxr/openxr_interface.h
@@ -37,6 +37,9 @@
#include "action_map/openxr_action_map.h"
#include "openxr_api.h"
+// declare some default strings
+#define INTERACTION_PROFILE_NONE "/interaction_profiles/none"
+
class OpenXRInterface : public XRInterface {
GDCLASS(OpenXRInterface, XRInterface);
@@ -54,40 +57,43 @@ private:
void _load_action_map();
- struct Action {
- String action_name;
- OpenXRAction::ActionType action_type;
- RID action_rid;
+ struct Action { // An action we've registered with OpenXR
+ String action_name; // Name of our action as presented to Godot (can be altered from the action map)
+ OpenXRAction::ActionType action_type; // The action type of this action
+ RID action_rid; // RID of the action registered with our OpenXR API
};
- struct ActionSet {
- String action_set_name;
- bool is_active;
- RID action_set_rid;
- Vector<Action *> actions;
+ struct ActionSet { // An action set we've registered with OpenXR
+ String action_set_name; // Name of our action set
+ bool is_active; // If true this action set is active and we will sync it
+ Vector<Action *> actions; // List of actions in this action set
+ RID action_set_rid; // RID of the action registered with our OpenXR API
};
- struct Tracker {
- String path_name;
- RID path_rid;
- Ref<XRPositionalTracker> positional_tracker;
- Vector<Action *> actions;
+ struct Tracker { // A tracker we've registered with OpenXR
+ String tracker_name; // Name of our tracker (can be altered from the action map)
+ Vector<Action *> actions; // Actions related to this tracker
+ Ref<XRPositionalTracker> positional_tracker; // Our positional tracker object that holds our tracker state
+ RID tracker_rid; // RID of the tracker registered with our OpenXR API
+ RID interaction_profile; // RID of the interaction profile bound to this tracker (can be null)
};
Vector<ActionSet *> action_sets;
+ Vector<RID> interaction_profiles;
Vector<Tracker *> trackers;
ActionSet *create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority);
void free_action_sets();
- Action *create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<RID> p_toplevel_paths);
+ Action *create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<Tracker *> p_trackers);
Action *find_action(const String &p_action_name);
void free_actions(ActionSet *p_action_set);
- Tracker *get_tracker(const String &p_path_name);
- Tracker *find_tracker(const String &p_positional_tracker_name);
+ Tracker *find_tracker(const String &p_tracker_name, bool p_create = false);
void link_action_to_tracker(Tracker *p_tracker, Action *p_action);
void handle_tracker(Tracker *p_tracker);
void free_trackers();
+ void free_interaction_profiles();
+
void _set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye);
protected:
@@ -97,6 +103,7 @@ public:
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
+ virtual PackedStringArray get_suggested_tracker_names() const override;
virtual TrackingStatus get_tracking_status() const override;
bool initialise_on_startup() const;
@@ -122,6 +129,13 @@ public:
virtual Vector<BlitToScreen> post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void end_frame() override;
+ void on_state_ready();
+ void on_state_visible();
+ void on_state_focused();
+ void on_state_stopping();
+ void on_pose_recentered();
+ void tracker_profile_changed(RID p_tracker, RID p_interaction_profile);
+
OpenXRInterface();
~OpenXRInterface();
};
diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp
index e515336daa..230b10c5f1 100644
--- a/modules/openxr/openxr_util.cpp
+++ b/modules/openxr/openxr_util.cpp
@@ -278,6 +278,20 @@ String OpenXRUtil::get_session_state_name(XrSessionState p_session_state) {
}
}
+String OpenXRUtil::get_action_type_name(XrActionType p_action_type) {
+ switch (p_action_type) {
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_BOOLEAN_INPUT)
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_FLOAT_INPUT)
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_VECTOR2F_INPUT)
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_POSE_INPUT)
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_VIBRATION_OUTPUT)
+ ENUM_TO_STRING_CASE(XR_ACTION_TYPE_MAX_ENUM)
+ default: {
+ return String("Action type ") + String::num_int64(int64_t(p_action_type));
+ } break;
+ }
+}
+
String OpenXRUtil::make_xr_version_string(XrVersion p_version) {
String version;
diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h
index 1261268376..4371b74d2f 100644
--- a/modules/openxr/openxr_util.h
+++ b/modules/openxr/openxr_util.h
@@ -40,6 +40,7 @@ public:
static String get_reference_space_name(XrReferenceSpaceType p_reference_space);
static String get_structure_type_name(XrStructureType p_structure_type);
static String get_session_state_name(XrSessionState p_session_state);
+ static String get_action_type_name(XrActionType p_action_type);
static String make_xr_version_string(XrVersion p_version);
};
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 86ff368619..7a74c8c089 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -75,9 +75,18 @@ void register_openxr_types() {
void unregister_openxr_types() {
if (openxr_interface.is_valid()) {
+ // uninitialise just in case
+ if (openxr_interface->is_initialized()) {
+ openxr_interface->uninitialize();
+ }
+
// unregister our interface from the XR server
- if (XRServer::get_singleton()) {
- XRServer::get_singleton()->remove_interface(openxr_interface);
+ XRServer *xr_server = XRServer::get_singleton();
+ if (xr_server) {
+ if (xr_server->get_primary_interface() == openxr_interface) {
+ xr_server->set_primary_interface(Ref<XRInterface>());
+ }
+ xr_server->remove_interface(openxr_interface);
}
// and release
diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp
index 671eb7a111..4857167a8e 100644
--- a/servers/xr/xr_positional_tracker.cpp
+++ b/servers/xr/xr_positional_tracker.cpp
@@ -49,6 +49,10 @@ void XRPositionalTracker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tracker_desc", "description"), &XRPositionalTracker::set_tracker_desc);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description"), "set_tracker_desc", "get_tracker_desc");
+ ClassDB::bind_method(D_METHOD("get_tracker_profile"), &XRPositionalTracker::get_tracker_profile);
+ ClassDB::bind_method(D_METHOD("set_tracker_profile", "profile"), &XRPositionalTracker::set_tracker_profile);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "profile"), "set_tracker_profile", "get_tracker_profile");
+
ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRPositionalTracker::get_tracker_hand);
ClassDB::bind_method(D_METHOD("set_tracker_hand", "hand"), &XRPositionalTracker::set_tracker_hand);
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Unknown,Left,Right"), "set_tracker_hand", "get_tracker_hand");
@@ -65,6 +69,7 @@ void XRPositionalTracker::_bind_methods() {
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, "vector")));
+ ADD_SIGNAL(MethodInfo("profile_changed", PropertyInfo(Variant::STRING, "role")));
};
void XRPositionalTracker::set_tracker_type(XRServer::TrackerType p_type) {
@@ -95,6 +100,18 @@ String XRPositionalTracker::get_tracker_desc() const {
return description;
}
+void XRPositionalTracker::set_tracker_profile(const String &p_profile) {
+ if (profile != p_profile) {
+ profile = p_profile;
+
+ emit_signal("profile_changed", profile);
+ }
+}
+
+String XRPositionalTracker::get_tracker_profile() const {
+ return profile;
+}
+
XRPositionalTracker::TrackerHand XRPositionalTracker::get_tracker_hand() const {
return hand;
};
diff --git a/servers/xr/xr_positional_tracker.h b/servers/xr/xr_positional_tracker.h
index ccb30bbbe6..cd06d4a087 100644
--- a/servers/xr/xr_positional_tracker.h
+++ b/servers/xr/xr_positional_tracker.h
@@ -56,7 +56,8 @@ public:
private:
XRServer::TrackerType type; // type of tracker
StringName name; // (unique) name of the tracker
- String description; // description of the tracker, this is interface dependent, for OpenXR this will be the interaction profile bound for to the tracker
+ String description; // description of the tracker
+ String profile; // this is interface dependent, for OpenXR this will be the interaction profile bound for to the tracker
TrackerHand hand; // if known, the hand this tracker is held in
Map<StringName, Ref<XRPose>> poses;
@@ -72,6 +73,8 @@ public:
StringName get_tracker_name() const;
void set_tracker_desc(const String &p_desc);
String get_tracker_desc() const;
+ void set_tracker_profile(const String &p_profile);
+ String get_tracker_profile() const;
XRPositionalTracker::TrackerHand get_tracker_hand() const;
void set_tracker_hand(const XRPositionalTracker::TrackerHand p_hand);