Path: blob/master/modules/openxr/openxr_interface.cpp
20892 views
/**************************************************************************/1/* openxr_interface.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "openxr_interface.h"3132#include "core/io/resource_loader.h"33#include "core/io/resource_saver.h"3435#include "extensions/openxr_eye_gaze_interaction.h"36#include "extensions/openxr_hand_interaction_extension.h"37#include "extensions/openxr_performance_settings_extension.h"38#include "extensions/openxr_user_presence_extension.h"39#include "servers/rendering/renderer_compositor.h"4041#include <openxr/openxr.h>4243void OpenXRInterface::_bind_methods() {44// lifecycle signals45ADD_SIGNAL(MethodInfo("session_begun"));46ADD_SIGNAL(MethodInfo("session_stopping"));47ADD_SIGNAL(MethodInfo("session_synchronized"));48ADD_SIGNAL(MethodInfo("session_focussed"));49ADD_SIGNAL(MethodInfo("session_visible"));50ADD_SIGNAL(MethodInfo("session_loss_pending"));51ADD_SIGNAL(MethodInfo("instance_exiting"));52ADD_SIGNAL(MethodInfo("pose_recentered"));53ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));5455ADD_SIGNAL(MethodInfo("cpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));56ADD_SIGNAL(MethodInfo("gpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));5758// State59ClassDB::bind_method(D_METHOD("get_session_state"), &OpenXRInterface::get_session_state);6061// User presence62ADD_SIGNAL(MethodInfo("user_presence_changed", PropertyInfo(Variant::BOOL, "is_user_present")));6364ClassDB::bind_method(D_METHOD("is_user_presence_supported"), &OpenXRInterface::is_user_presence_supported);65ClassDB::bind_method(D_METHOD("is_user_present"), &OpenXRInterface::is_user_present);6667// Display refresh rate68ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate);69ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate);70ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");7172// Render Target size multiplier73ClassDB::bind_method(D_METHOD("get_render_target_size_multiplier"), &OpenXRInterface::get_render_target_size_multiplier);74ClassDB::bind_method(D_METHOD("set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier);75ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier");7677// Foveation level78ClassDB::bind_method(D_METHOD("is_foveation_supported"), &OpenXRInterface::is_foveation_supported);7980ClassDB::bind_method(D_METHOD("get_foveation_level"), &OpenXRInterface::get_foveation_level);81ClassDB::bind_method(D_METHOD("set_foveation_level", "foveation_level"), &OpenXRInterface::set_foveation_level);82ADD_PROPERTY(PropertyInfo(Variant::INT, "foveation_level"), "set_foveation_level", "get_foveation_level");8384ClassDB::bind_method(D_METHOD("get_foveation_dynamic"), &OpenXRInterface::get_foveation_dynamic);85ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic);86ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic");8788// Action sets89ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active);90ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active);91ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets);9293// Refresh rates94ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);9596// Hand tracking.97ClassDB::bind_method(D_METHOD("set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range);98ClassDB::bind_method(D_METHOD("get_motion_range", "hand"), &OpenXRInterface::get_motion_range);99100ClassDB::bind_method(D_METHOD("get_hand_tracking_source", "hand"), &OpenXRInterface::get_hand_tracking_source);101102ClassDB::bind_method(D_METHOD("get_hand_joint_flags", "hand", "joint"), &OpenXRInterface::get_hand_joint_flags);103104ClassDB::bind_method(D_METHOD("get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation);105ClassDB::bind_method(D_METHOD("get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position);106ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius);107108ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity);109ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);110111ClassDB::bind_method(D_METHOD("is_hand_tracking_supported"), &OpenXRInterface::is_hand_tracking_supported);112ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported);113ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);114115// VRS116ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius);117ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius);118ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength);119ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength);120121// Performance settings.122ClassDB::bind_method(D_METHOD("set_cpu_level", "level"), &OpenXRInterface::set_cpu_level);123ClassDB::bind_method(D_METHOD("set_gpu_level", "level"), &OpenXRInterface::set_gpu_level);124125ADD_GROUP("Vulkan VRS", "vrs_");126ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");127ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");128129BIND_ENUM_CONSTANT(SESSION_STATE_UNKNOWN);130BIND_ENUM_CONSTANT(SESSION_STATE_IDLE);131BIND_ENUM_CONSTANT(SESSION_STATE_READY);132BIND_ENUM_CONSTANT(SESSION_STATE_SYNCHRONIZED);133BIND_ENUM_CONSTANT(SESSION_STATE_VISIBLE);134BIND_ENUM_CONSTANT(SESSION_STATE_FOCUSED);135BIND_ENUM_CONSTANT(SESSION_STATE_STOPPING);136BIND_ENUM_CONSTANT(SESSION_STATE_LOSS_PENDING);137BIND_ENUM_CONSTANT(SESSION_STATE_EXITING);138139BIND_ENUM_CONSTANT(HAND_LEFT);140BIND_ENUM_CONSTANT(HAND_RIGHT);141BIND_ENUM_CONSTANT(HAND_MAX);142143BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_UNOBSTRUCTED);144BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER);145BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_MAX);146147BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNKNOWN);148BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNOBSTRUCTED);149BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_CONTROLLER);150BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_MAX);151152BIND_ENUM_CONSTANT(HAND_JOINT_PALM);153BIND_ENUM_CONSTANT(HAND_JOINT_WRIST);154BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL);155BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PROXIMAL);156BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_DISTAL);157BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP);158BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_METACARPAL);159BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_PROXIMAL);160BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_INTERMEDIATE);161BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_DISTAL);162BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_TIP);163BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_METACARPAL);164BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_PROXIMAL);165BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_INTERMEDIATE);166BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_DISTAL);167BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_TIP);168BIND_ENUM_CONSTANT(HAND_JOINT_RING_METACARPAL);169BIND_ENUM_CONSTANT(HAND_JOINT_RING_PROXIMAL);170BIND_ENUM_CONSTANT(HAND_JOINT_RING_INTERMEDIATE);171BIND_ENUM_CONSTANT(HAND_JOINT_RING_DISTAL);172BIND_ENUM_CONSTANT(HAND_JOINT_RING_TIP);173BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_METACARPAL);174BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_PROXIMAL);175BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_INTERMEDIATE);176BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_DISTAL);177BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP);178BIND_ENUM_CONSTANT(HAND_JOINT_MAX);179180BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_POWER_SAVINGS);181BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_LOW);182BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_HIGH);183BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_BOOST);184185BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_COMPOSITING);186BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_RENDERING);187BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_THERMAL);188189BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_NORMAL);190BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_WARNING);191BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED);192193BIND_BITFIELD_FLAG(HAND_JOINT_NONE);194BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_VALID);195BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_TRACKED);196BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_VALID);197BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_TRACKED);198BIND_BITFIELD_FLAG(HAND_JOINT_LINEAR_VELOCITY_VALID);199BIND_BITFIELD_FLAG(HAND_JOINT_ANGULAR_VELOCITY_VALID);200}201202StringName OpenXRInterface::get_name() const {203return StringName("OpenXR");204}205206uint32_t OpenXRInterface::get_capabilities() const {207return XRInterface::XR_VR + XRInterface::XR_STEREO;208}209210PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {211// These are hardcoded in OpenXR, note that they will only be available if added to our action map212213PackedStringArray arr = {214"head", // XRPositionalTracker for the users head (Mapped from OpenXR /user/head)215"left_hand", // XRControllerTracker for the users left hand (Mapped from OpenXR /user/hand/left)216"right_hand", // XRControllerTracker for the users right hand (Mapped from OpenXR /user/hand/right)217"/user/hand_tracker/left", // XRHandTracker for the users left hand218"/user/hand_tracker/right", // XRHandTracker for the users right hand219"/user/body_tracker", // XRBodyTracker for the users body220"/user/face_tracker", // XRFaceTracker for the users face221"/user/treadmill"222};223224for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {225arr.append_array(wrapper->get_suggested_tracker_names());226}227228return arr;229}230231XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const {232return tracking_state;233}234235void OpenXRInterface::_load_action_map() {236ERR_FAIL_NULL(openxr_api);237238// This may seem a bit duplicitous to a little bit of background info here.239// OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in.240// This gives the user the ability to edit the action map in a UI and customize the actions.241// OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it.242// This system does that push and we store the info needed to then work with this action map going forward.243244// Within our openxr device we maintain a number of classes that wrap the relevant OpenXR objects for this.245// Within OpenXRInterface we have a few internal classes that keep track of what we've created.246// This allow us to process the relevant actions each frame.247248// just in case clean up249free_trackers();250free_interaction_profiles();251free_action_sets();252253Ref<OpenXRActionMap> action_map;254if (Engine::get_singleton()->is_editor_hint()) {255#ifdef TOOLS_ENABLED256action_map.instantiate();257action_map->create_editor_action_sets();258#endif259} else {260String default_tres_name = openxr_api->get_default_action_map_resource_name();261262// Check if we can load our default263if (ResourceLoader::exists(default_tres_name)) {264action_map = ResourceLoader::load(default_tres_name);265}266267// Check if we need to create default action set268if (action_map.is_null()) {269action_map.instantiate();270action_map->create_default_action_sets();271#ifdef TOOLS_ENABLED272// Save our action sets so our user can273action_map->set_path(default_tres_name, true);274ResourceSaver::save(action_map, default_tres_name);275#endif276}277}278279// process our action map280if (action_map.is_valid()) {281HashMap<Ref<OpenXRAction>, Action *> xr_actions;282283Array action_set_array = action_map->get_action_sets();284for (int i = 0; i < action_set_array.size(); i++) {285// Create our action set286Ref<OpenXRActionSet> xr_action_set = action_set_array[i];287ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority());288if (!action_set) {289continue;290}291292// Now create our actions for these293Array actions = xr_action_set->get_actions();294for (int j = 0; j < actions.size(); j++) {295Ref<OpenXRAction> xr_action = actions[j];296297PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();298Vector<Tracker *> trackers_for_action;299300for (int k = 0; k < toplevel_paths.size(); k++) {301// Only check for our tracker if our path is supported.302if (openxr_api->is_top_level_path_supported(toplevel_paths[k])) {303Tracker *tracker = find_tracker(toplevel_paths[k], true);304if (tracker) {305trackers_for_action.push_back(tracker);306}307}308}309310// Only add our action if we have at least one valid toplevel path311if (trackers_for_action.size() > 0) {312Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action);313if (action) {314// add this to our map for creating our interaction profiles315xr_actions[xr_action] = action;316}317}318}319}320321// now do our suggestions322Array interaction_profile_array = action_map->get_interaction_profiles();323for (int i = 0; i < interaction_profile_array.size(); i++) {324Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i];325326// Note, we can only have one entry per interaction profile so if it already exists we clear it out327RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path());328if (ip.is_valid()) {329openxr_api->interaction_profile_clear_bindings(ip);330331for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_interaction_profile->get_binding_modifiers()) {332PackedByteArray bm = xr_binding_modifier->get_ip_modification();333if (!bm.is_empty()) {334openxr_api->interaction_profile_add_modifier(ip, bm);335}336}337338Array xr_bindings = xr_interaction_profile->get_bindings();339for (int j = 0; j < xr_bindings.size(); j++) {340Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];341Ref<OpenXRAction> xr_action = xr_binding->get_action();342343Action *action = nullptr;344if (xr_actions.has(xr_action)) {345action = xr_actions[xr_action];346} else {347print_line("Action ", xr_action->get_name(), " isn't part of an action set!");348continue;349}350351int binding_no = openxr_api->interaction_profile_add_binding(ip, action->action_rid, xr_binding->get_binding_path());352if (binding_no >= 0) {353for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_binding->get_binding_modifiers()) {354// Binding modifiers on bindings can be added to the interaction profile.355PackedByteArray bm = xr_binding_modifier->get_ip_modification();356if (!bm.is_empty()) {357openxr_api->interaction_profile_add_modifier(ip, bm);358}359360// And possibly in the future on the binding itself, we're just preparing for that eventuality.361}362}363}364365// Now submit our suggestions366openxr_api->interaction_profile_suggest_bindings(ip);367368// And record it in our array so we can clean it up later on369if (interaction_profile_array.has(ip)) {370interaction_profile_array.push_back(ip);371}372}373}374}375}376377OpenXRInterface::ActionSet *OpenXRInterface::create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority) {378ERR_FAIL_NULL_V(openxr_api, nullptr);379380// find if it already exists381for (int i = 0; i < action_sets.size(); i++) {382if (action_sets[i]->action_set_name == p_action_set_name) {383// already exists in this set384return nullptr;385}386}387388ActionSet *action_set = memnew(ActionSet);389action_set->action_set_name = p_action_set_name;390action_set->is_active = true;391action_set->action_set_rid = openxr_api->action_set_create(p_action_set_name, p_localized_name, p_priority);392action_sets.push_back(action_set);393394return action_set;395}396397void OpenXRInterface::free_action_sets() {398ERR_FAIL_NULL(openxr_api);399400for (int i = 0; i < action_sets.size(); i++) {401ActionSet *action_set = action_sets[i];402403free_actions(action_set);404405openxr_api->action_set_free(action_set->action_set_rid);406407memfree(action_set);408}409action_sets.clear();410}411412OpenXRInterface::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) {413ERR_FAIL_NULL_V(openxr_api, nullptr);414415for (int i = 0; i < p_action_set->actions.size(); i++) {416if (p_action_set->actions[i]->action_name == p_action_name) {417// already exists in this set418return nullptr;419}420}421422Vector<RID> tracker_rids;423for (int i = 0; i < p_trackers.size(); i++) {424tracker_rids.push_back(p_trackers[i]->tracker_rid);425}426427Action *action = memnew(Action);428if (p_action_type == OpenXRAction::OPENXR_ACTION_POSE) {429// We can't have dual action names in OpenXR hence we added _pose,430// but default, aim and grip and default pose action names in Godot so rename them on the tracker.431// NOTE need to decide on whether we should keep the naming convention or rename it on Godots side432if (p_action_name == "default_pose") {433action->action_name = "default";434} else if (p_action_name == "aim_pose") {435action->action_name = "aim";436} else if (p_action_name == "grip_pose") {437action->action_name = "grip";438} else {439action->action_name = p_action_name;440}441} else {442action->action_name = p_action_name;443}444445action->action_type = p_action_type;446action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids);447p_action_set->actions.push_back(action);448449// we link our actions back to our trackers so we know which actions to check when we're processing our trackers450for (int i = 0; i < p_trackers.size(); i++) {451if (!p_trackers[i]->actions.has(action)) {452p_trackers[i]->actions.push_back(action);453}454}455456return action;457}458459OpenXRInterface::Action *OpenXRInterface::find_action(const String &p_action_name) {460// We just find the first action by this name461462for (int i = 0; i < action_sets.size(); i++) {463for (int j = 0; j < action_sets[i]->actions.size(); j++) {464if (action_sets[i]->actions[j]->action_name == p_action_name) {465return action_sets[i]->actions[j];466}467}468}469470// not found471return nullptr;472}473474void OpenXRInterface::free_actions(ActionSet *p_action_set) {475ERR_FAIL_NULL(openxr_api);476477for (int i = 0; i < p_action_set->actions.size(); i++) {478Action *action = p_action_set->actions[i];479480openxr_api->action_free(action->action_rid);481482memdelete(action);483}484p_action_set->actions.clear();485}486487OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_name, bool p_create) {488XRServer *xr_server = XRServer::get_singleton();489ERR_FAIL_NULL_V(xr_server, nullptr);490ERR_FAIL_NULL_V(openxr_api, nullptr);491492Tracker *tracker = nullptr;493for (int i = 0; i < trackers.size(); i++) {494tracker = trackers[i];495if (tracker->tracker_name == p_tracker_name) {496return tracker;497}498}499500if (!p_create) {501return nullptr;502}503504ERR_FAIL_COND_V(!openxr_api->is_top_level_path_supported(p_tracker_name), nullptr);505506// Create our RID507RID tracker_rid = openxr_api->tracker_create(p_tracker_name);508ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);509510// Create our controller tracker.511Ref<XRControllerTracker> controller_tracker;512controller_tracker.instantiate();513514// We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these.515if (p_tracker_name == "/user/hand/left") {516controller_tracker->set_tracker_name("left_hand");517controller_tracker->set_tracker_desc("Left hand controller");518controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);519} else if (p_tracker_name == "/user/hand/right") {520controller_tracker->set_tracker_name("right_hand");521controller_tracker->set_tracker_desc("Right hand controller");522controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);523} else {524controller_tracker->set_tracker_name(p_tracker_name);525controller_tracker->set_tracker_desc(p_tracker_name);526}527controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);528xr_server->add_tracker(controller_tracker);529530// create a new entry531tracker = memnew(Tracker);532tracker->tracker_name = p_tracker_name;533tracker->tracker_rid = tracker_rid;534tracker->controller_tracker = controller_tracker;535tracker->interaction_profile = RID();536trackers.push_back(tracker);537538return tracker;539}540541void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_profile) {542Tracker *tracker = nullptr;543for (int i = 0; i < trackers.size() && tracker == nullptr; i++) {544if (trackers[i]->tracker_rid == p_tracker) {545tracker = trackers[i];546}547}548ERR_FAIL_NULL(tracker);549550tracker->interaction_profile = p_interaction_profile;551552if (p_interaction_profile.is_null()) {553print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + INTERACTION_PROFILE_NONE);554tracker->controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);555} else {556String name = openxr_api->interaction_profile_get_name(p_interaction_profile);557print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + name);558tracker->controller_tracker->set_tracker_profile(name);559}560}561562void OpenXRInterface::handle_tracker(Tracker *p_tracker) {563ERR_FAIL_NULL(openxr_api);564ERR_FAIL_COND(p_tracker->controller_tracker.is_null());565566// Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction567// profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these568// and rebind them or even offer bindings to controllers that are not known to us.569570// We don't really have a consistent way to detect whether a controller is active however as long as it is571// unbound it seems to be unavailable, so far unknown controller seem to mimic one of the profiles we've572// supplied.573if (p_tracker->interaction_profile.is_null()) {574return;575}576577// We check all actions that are related to our tracker.578for (int i = 0; i < p_tracker->actions.size(); i++) {579Action *action = p_tracker->actions[i];580switch (action->action_type) {581case OpenXRAction::OPENXR_ACTION_BOOL: {582bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid);583p_tracker->controller_tracker->set_input(action->action_name, Variant(pressed));584} break;585case OpenXRAction::OPENXR_ACTION_FLOAT: {586real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid);587p_tracker->controller_tracker->set_input(action->action_name, Variant(value));588} break;589case OpenXRAction::OPENXR_ACTION_VECTOR2: {590Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid);591p_tracker->controller_tracker->set_input(action->action_name, Variant(value));592} break;593case OpenXRAction::OPENXR_ACTION_POSE: {594Transform3D transform;595Vector3 linear, angular;596597XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular);598599if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {600p_tracker->controller_tracker->set_pose(action->action_name, transform, linear, angular, confidence);601} else {602p_tracker->controller_tracker->invalidate_pose(action->action_name);603}604} break;605default: {606// not yet supported607} break;608}609}610}611612void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) {613ERR_FAIL_NULL(openxr_api);614615Action *action = find_action(p_action_name);616ERR_FAIL_NULL(action);617618// We need to map our tracker name to our OpenXR name for our inbuild names.619String tracker_name = p_tracker_name;620if (tracker_name == "left_hand") {621tracker_name = "/user/hand/left";622} else if (tracker_name == "right_hand") {623tracker_name = "/user/hand/right";624}625Tracker *tracker = find_tracker(tracker_name);626ERR_FAIL_NULL(tracker);627628// TODO OpenXR does not support delay, so we may need to add support for that somehow...629630XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds631632openxr_api->trigger_haptic_pulse(action->action_rid, tracker->tracker_rid, p_frequency, p_amplitude, duration);633}634635void OpenXRInterface::free_trackers() {636XRServer *xr_server = XRServer::get_singleton();637ERR_FAIL_NULL(xr_server);638ERR_FAIL_NULL(openxr_api);639640for (int i = 0; i < trackers.size(); i++) {641Tracker *tracker = trackers[i];642643openxr_api->tracker_free(tracker->tracker_rid);644xr_server->remove_tracker(tracker->controller_tracker);645tracker->controller_tracker.unref();646647memdelete(tracker);648}649trackers.clear();650}651652void OpenXRInterface::free_interaction_profiles() {653ERR_FAIL_NULL(openxr_api);654655for (const RID &interaction_profile : interaction_profiles) {656openxr_api->interaction_profile_free(interaction_profile);657}658interaction_profiles.clear();659}660661bool OpenXRInterface::initialize_on_startup() const {662if (openxr_api == nullptr) {663return false;664} else if (!openxr_api->is_initialized()) {665return false;666} else {667return true;668}669}670671bool OpenXRInterface::is_initialized() const {672return initialized;673}674675bool OpenXRInterface::initialize() {676XRServer *xr_server = XRServer::get_singleton();677ERR_FAIL_NULL_V(xr_server, false);678679if (openxr_api == nullptr) {680return false;681} else if (!openxr_api->is_initialized()) {682return false;683} else if (initialized) {684return true;685}686687// load up our action sets before setting up our session, note that our profiles are suggestions, OpenXR takes ownership of (re)binding688_load_action_map();689690if (!openxr_api->initialize_session()) {691return false;692}693694// we must create a tracker for our head695head.instantiate();696head->set_tracker_type(XRServer::TRACKER_HEAD);697head->set_tracker_name("head");698head->set_tracker_desc("Players head");699xr_server->add_tracker(head);700701// attach action sets702Vector<RID> loaded_action_sets;703for (int i = 0; i < action_sets.size(); i++) {704loaded_action_sets.append(action_sets[i]->action_set_rid);705}706openxr_api->attach_action_sets(loaded_action_sets);707708// make this our primary interface709xr_server->set_primary_interface(this);710711// Register an additional output with the display server, so rendering won't712// be skipped if no windows are visible.713DisplayServer::get_singleton()->register_additional_output(this);714715initialized = true;716717return initialized;718}719720void OpenXRInterface::uninitialize() {721// Our OpenXR driver will clean itself up properly when Godot exits, so we just do some basic stuff here722723// end the session if we need to?724725// cleanup stuff726free_trackers();727free_interaction_profiles();728free_action_sets();729730XRServer *xr_server = XRServer::get_singleton();731if (xr_server) {732if (head.is_valid()) {733xr_server->remove_tracker(head);734head.unref();735}736}737738DisplayServer::get_singleton()->unregister_additional_output(this);739740initialized = false;741}742743Dictionary OpenXRInterface::get_system_info() {744Dictionary dict;745746if (openxr_api) {747dict[SNAME("XRRuntimeName")] = openxr_api->get_runtime_name();748dict[SNAME("XRRuntimeVersion")] = openxr_api->get_runtime_version();749dict[SNAME("OpenXRSystemName")] = openxr_api->get_system_name();750dict[SNAME("OpenXRVendorID")] = openxr_api->get_vendor_id();751}752753return dict;754}755756bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {757if (p_mode == XRInterface::XR_PLAY_AREA_3DOF) {758return false;759}760return true;761}762763XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {764if (!openxr_api || !initialized) {765return XRInterface::XR_PLAY_AREA_UNKNOWN;766}767768XrReferenceSpaceType reference_space = openxr_api->get_reference_space();769770if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL) {771return XRInterface::XR_PLAY_AREA_SITTING;772} else if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT) {773return XRInterface::XR_PLAY_AREA_ROOMSCALE;774} else if (reference_space == XR_REFERENCE_SPACE_TYPE_STAGE) {775return XRInterface::XR_PLAY_AREA_STAGE;776} else if (reference_space == XR_REFERENCE_SPACE_TYPE_MAX_ENUM) {777return XRInterface::XR_PLAY_AREA_CUSTOM;778}779780return XRInterface::XR_PLAY_AREA_UNKNOWN;781}782783bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {784ERR_FAIL_NULL_V(openxr_api, false);785786XrReferenceSpaceType reference_space;787788if (p_mode == XRInterface::XR_PLAY_AREA_SITTING) {789reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;790} else if (p_mode == XRInterface::XR_PLAY_AREA_ROOMSCALE) {791reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;792} else if (p_mode == XRInterface::XR_PLAY_AREA_STAGE) {793reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;794} else {795return false;796}797798if (openxr_api->set_requested_reference_space(reference_space)) {799XRServer *xr_server = XRServer::get_singleton();800if (xr_server) {801xr_server->clear_reference_frame();802}803return true;804}805806return false;807}808809PackedVector3Array OpenXRInterface::get_play_area() const {810XRServer *xr_server = XRServer::get_singleton();811ERR_FAIL_NULL_V(xr_server, PackedVector3Array());812PackedVector3Array arr;813814Vector3 sides[4] = {815Vector3(-0.5f, 0.0f, -0.5f),816Vector3(0.5f, 0.0f, -0.5f),817Vector3(0.5f, 0.0f, 0.5f),818Vector3(-0.5f, 0.0f, 0.5f),819};820821if (openxr_api != nullptr && openxr_api->is_initialized()) {822Size2 extents = openxr_api->get_play_space_bounds();823if (extents.width != 0.0 && extents.height != 0.0) {824Transform3D reference_frame = xr_server->get_reference_frame();825826for (int i = 0; i < 4; i++) {827Vector3 coord = sides[i];828829// Scale it up.830coord.x *= extents.width;831coord.z *= extents.height;832833// Now apply our reference.834Vector3 out = reference_frame.xform(coord);835arr.push_back(out);836}837} else {838WARN_PRINT_ONCE("OpenXR: No extents available.");839}840}841842return arr;843}844845float OpenXRInterface::get_display_refresh_rate() const {846if (openxr_api == nullptr) {847return 0.0;848} else if (!openxr_api->is_initialized()) {849return 0.0;850} else {851return openxr_api->get_display_refresh_rate();852}853}854855void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) {856if (openxr_api == nullptr) {857return;858} else if (!openxr_api->is_initialized()) {859return;860} else {861openxr_api->set_display_refresh_rate(p_refresh_rate);862}863}864865Array OpenXRInterface::get_available_display_refresh_rates() const {866if (openxr_api == nullptr) {867return Array();868} else if (!openxr_api->is_initialized()) {869return Array();870} else {871return openxr_api->get_available_display_refresh_rates();872}873}874875bool OpenXRInterface::is_hand_tracking_supported() {876if (openxr_api == nullptr) {877return false;878} else if (!openxr_api->is_initialized()) {879return false;880} else {881OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();882if (hand_tracking_ext == nullptr) {883return false;884} else {885return hand_tracking_ext->get_active();886}887}888}889890bool OpenXRInterface::is_hand_interaction_supported() const {891if (openxr_api == nullptr) {892return false;893} else if (!openxr_api->is_initialized()) {894return false;895} else {896OpenXRHandInteractionExtension *hand_interaction_ext = OpenXRHandInteractionExtension::get_singleton();897if (hand_interaction_ext == nullptr) {898return false;899} else {900return hand_interaction_ext->is_available();901}902}903}904905bool OpenXRInterface::is_eye_gaze_interaction_supported() {906if (openxr_api == nullptr) {907return false;908} else if (!openxr_api->is_initialized()) {909return false;910} else {911OpenXREyeGazeInteractionExtension *eye_gaze_ext = OpenXREyeGazeInteractionExtension::get_singleton();912if (eye_gaze_ext == nullptr) {913return false;914} else {915return eye_gaze_ext->supports_eye_gaze_interaction();916}917}918}919920bool OpenXRInterface::is_action_set_active(const String &p_action_set) const {921for (ActionSet *action_set : action_sets) {922if (action_set->action_set_name == p_action_set) {923return action_set->is_active;924}925}926927WARN_PRINT("OpenXR: Unknown action set " + p_action_set);928return false;929}930931void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) {932for (ActionSet *action_set : action_sets) {933if (action_set->action_set_name == p_action_set) {934action_set->is_active = p_active;935return;936}937}938939WARN_PRINT("OpenXR: Unknown action set " + p_action_set);940}941942Array OpenXRInterface::get_action_sets() const {943Array arr;944945for (ActionSet *action_set : action_sets) {946arr.push_back(action_set->action_set_name);947}948949return arr;950}951952float OpenXRInterface::get_vrs_min_radius() const {953return xr_vrs.get_vrs_min_radius();954}955956void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) {957xr_vrs.set_vrs_min_radius(p_vrs_min_radius);958}959960float OpenXRInterface::get_vrs_strength() const {961return xr_vrs.get_vrs_strength();962}963964void OpenXRInterface::set_vrs_strength(float p_vrs_strength) {965xr_vrs.set_vrs_strength(p_vrs_strength);966}967968double OpenXRInterface::get_render_target_size_multiplier() const {969if (openxr_api == nullptr) {970return 1.0;971} else {972return openxr_api->get_render_target_size_multiplier();973}974}975976void OpenXRInterface::set_render_target_size_multiplier(double multiplier) {977if (openxr_api == nullptr) {978return;979} else {980openxr_api->set_render_target_size_multiplier(multiplier);981}982}983984bool OpenXRInterface::is_foveation_supported() const {985if (openxr_api == nullptr) {986return false;987} else {988return openxr_api->is_foveation_supported();989}990}991992int OpenXRInterface::get_foveation_level() const {993if (openxr_api == nullptr) {994return 0;995} else {996return openxr_api->get_foveation_level();997}998}9991000void OpenXRInterface::set_foveation_level(int p_foveation_level) {1001if (openxr_api == nullptr) {1002return;1003} else {1004openxr_api->set_foveation_level(p_foveation_level);1005}1006}10071008bool OpenXRInterface::get_foveation_dynamic() const {1009if (openxr_api == nullptr) {1010return false;1011} else {1012return openxr_api->get_foveation_dynamic();1013}1014}10151016void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) {1017if (openxr_api == nullptr) {1018return;1019} else {1020openxr_api->set_foveation_dynamic(p_foveation_dynamic);1021}1022}10231024Size2 OpenXRInterface::get_render_target_size() {1025if (openxr_api == nullptr) {1026return Size2();1027} else {1028return openxr_api->get_recommended_target_size();1029}1030}10311032uint32_t OpenXRInterface::get_view_count() {1033// TODO set this based on our configuration1034return 2;1035}10361037void OpenXRInterface::_set_default_pos(Transform3D &r_transform, double p_world_scale, uint64_t p_eye) {1038r_transform = Transform3D();10391040// if we're not tracking, don't put our head on the floor...1041r_transform.origin.y = 1.5 * p_world_scale;10421043// overkill but..1044if (p_eye == 1) {1045r_transform.origin.x = 0.03 * p_world_scale;1046} else if (p_eye == 2) {1047r_transform.origin.x = -0.03 * p_world_scale;1048}1049}10501051Transform3D OpenXRInterface::get_camera_transform() {1052XRServer *xr_server = XRServer::get_singleton();1053ERR_FAIL_NULL_V(xr_server, Transform3D());10541055Transform3D hmd_transform;1056double world_scale = xr_server->get_world_scale();10571058// head_transform should be updated in process10591060hmd_transform.basis = head_transform.basis;1061hmd_transform.origin = head_transform.origin * world_scale;10621063return hmd_transform;1064}10651066Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {1067XRServer *xr_server = XRServer::get_singleton();1068ERR_FAIL_NULL_V(xr_server, Transform3D());1069ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds.");10701071Transform3D t;1072if (openxr_api && openxr_api->get_view_transform(p_view, t)) {1073// update our cached value if we have a valid transform1074transform_for_view[p_view] = t;1075} else {1076// reuse cached value1077t = transform_for_view[p_view];1078}10791080// Apply our world scale1081double world_scale = xr_server->get_world_scale();1082t.origin *= world_scale;10831084return p_cam_transform * xr_server->get_reference_frame() * t;1085}10861087Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {1088Projection cm;1089ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds.");10901091if (openxr_api) {1092if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {1093return cm;1094}1095}10961097// Failed to get from our OpenXR device? Default to some sort of sensible camera matrix..1098cm.set_for_hmd(p_view + 1, 1.0, 6.0, 14.5, 4.0, 1.5, p_z_near, p_z_far);10991100return cm;1101}11021103Rect2i OpenXRInterface::get_render_region() {1104if (openxr_api) {1105return openxr_api->get_render_region();1106} else {1107return Rect2i();1108}1109}11101111RID OpenXRInterface::get_color_texture() {1112if (openxr_api) {1113return openxr_api->get_color_texture();1114} else {1115return RID();1116}1117}11181119RID OpenXRInterface::get_depth_texture() {1120if (openxr_api) {1121return openxr_api->get_depth_texture();1122} else {1123return RID();1124}1125}11261127RID OpenXRInterface::get_velocity_texture() {1128if (openxr_api) {1129return openxr_api->get_velocity_texture();1130} else {1131return RID();1132}1133}11341135RID OpenXRInterface::get_velocity_depth_texture() {1136if (openxr_api) {1137return openxr_api->get_velocity_depth_texture();1138} else {1139return RID();1140}1141}11421143Size2i OpenXRInterface::get_velocity_target_size() {1144if (openxr_api) {1145return openxr_api->get_velocity_target_size();1146} else {1147return Size2i();1148}1149}11501151void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrackingExtension::HandTrackedHands p_hand) {1152OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1153if (hand_tracking_ext && hand_tracking_ext->get_active()) {1154OpenXRInterface::Tracker *tracker = find_tracker(p_path);1155if (tracker && tracker->controller_tracker.is_valid()) {1156XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);11571158if (location_flags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT)) {1159static const XrSpaceLocationFlags all_location_flags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT + XR_SPACE_LOCATION_POSITION_TRACKED_BIT;1160XRPose::TrackingConfidence confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;1161Transform3D transform;1162Vector3 linear_velocity;1163Vector3 angular_velocity;11641165if ((location_flags & all_location_flags) == all_location_flags) {1166// All flags set? confidence is high!1167confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;1168}11691170if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {1171transform.basis = Basis(hand_tracking_ext->get_hand_joint_rotation(p_hand, XR_HAND_JOINT_PALM_EXT));1172}1173if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {1174transform.origin = hand_tracking_ext->get_hand_joint_position(p_hand, XR_HAND_JOINT_PALM_EXT);1175}11761177XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);1178if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {1179linear_velocity = hand_tracking_ext->get_hand_joint_linear_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);1180}1181if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {1182angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);1183}11841185tracker->controller_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);1186} else {1187tracker->controller_tracker->invalidate_pose("skeleton");1188}1189}1190}1191}11921193void OpenXRInterface::process() {1194if (openxr_api) {1195// do our normal process1196if (openxr_api->process()) {1197Transform3D t;1198Vector3 linear_velocity;1199Vector3 angular_velocity;1200head_confidence = openxr_api->get_head_center(t, linear_velocity, angular_velocity);1201if (head_confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {1202// Only update our transform if we have one to update it with1203// note that poses are stored without world scale and reference frame applied!1204head_transform = t;1205head_linear_velocity = linear_velocity;1206head_angular_velocity = angular_velocity;1207}1208}12091210// handle our action sets....1211Vector<RID> active_sets;1212for (int i = 0; i < action_sets.size(); i++) {1213if (action_sets[i]->is_active) {1214active_sets.push_back(action_sets[i]->action_set_rid);1215}1216}12171218if (openxr_api->sync_action_sets(active_sets)) {1219for (int i = 0; i < trackers.size(); i++) {1220handle_tracker(trackers[i]);1221}1222}12231224// Handle hand tracking1225handle_hand_tracking("/user/hand/left", OpenXRHandTrackingExtension::OPENXR_TRACKED_LEFT_HAND);1226handle_hand_tracking("/user/hand/right", OpenXRHandTrackingExtension::OPENXR_TRACKED_RIGHT_HAND);1227}12281229if (head.is_valid()) {1230head->set_pose("default", head_transform, head_linear_velocity, head_angular_velocity, head_confidence);1231}12321233if (reference_stage_changing) {1234// Now that we have updated tracking information in our updated reference space, trigger our pose recentered signal.1235emit_signal(SNAME("pose_recentered"));1236reference_stage_changing = false;1237}1238}12391240void OpenXRInterface::pre_render() {1241if (openxr_api) {1242openxr_api->pre_render();1243}1244}12451246bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {1247if (openxr_api) {1248return openxr_api->pre_draw_viewport(p_render_target);1249} else {1250// don't render1251return false;1252}1253}12541255Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {1256Vector<BlitToScreen> blit_to_screen;12571258#ifndef ANDROID_ENABLED1259// If separate HMD we should output one eye to screen1260if (p_screen_rect != Rect2()) {1261BlitToScreen blit;12621263blit.render_target = p_render_target;1264blit.multi_view.use_layer = true;1265blit.multi_view.layer = 0;1266blit.lens_distortion.apply = false;12671268Size2 render_size = get_render_target_size();1269Rect2 dst_rect = p_screen_rect;1270float new_height = dst_rect.size.x * (render_size.y / render_size.x);1271if (new_height > dst_rect.size.y) {1272dst_rect.position.y = (0.5 * dst_rect.size.y) - (0.5 * new_height);1273dst_rect.size.y = new_height;1274} else {1275float new_width = dst_rect.size.y * (render_size.x / render_size.y);12761277dst_rect.position.x = (0.5 * dst_rect.size.x) - (0.5 * new_width);1278dst_rect.size.x = new_width;1279}12801281blit.dst_rect = dst_rect;1282blit_to_screen.push_back(blit);1283}1284#endif12851286if (openxr_api) {1287openxr_api->post_draw_viewport(p_render_target);1288}12891290return blit_to_screen;1291}12921293void OpenXRInterface::end_frame() {1294if (openxr_api) {1295openxr_api->end_frame();1296}1297}12981299bool OpenXRInterface::is_passthrough_supported() {1300return get_supported_environment_blend_modes().find(XR_ENV_BLEND_MODE_ALPHA_BLEND);1301}13021303bool OpenXRInterface::is_passthrough_enabled() {1304return get_environment_blend_mode() == XR_ENV_BLEND_MODE_ALPHA_BLEND;1305}13061307bool OpenXRInterface::start_passthrough() {1308return set_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND);1309}13101311void OpenXRInterface::stop_passthrough() {1312set_environment_blend_mode(XR_ENV_BLEND_MODE_OPAQUE);1313}13141315Array OpenXRInterface::get_supported_environment_blend_modes() {1316if (!openxr_api) {1317return Array();1318}1319Array modes;13201321const Vector<XrEnvironmentBlendMode> env_blend_modes = openxr_api->get_supported_environment_blend_modes();13221323for (const XrEnvironmentBlendMode &env_blend_mode : env_blend_modes) {1324switch (env_blend_mode) {1325case XR_ENVIRONMENT_BLEND_MODE_OPAQUE:1326modes.push_back(XR_ENV_BLEND_MODE_OPAQUE);1327break;1328case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE:1329modes.push_back(XR_ENV_BLEND_MODE_ADDITIVE);1330break;1331case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND:1332modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);1333break;1334default:1335WARN_PRINT(vformat("Unsupported blend mode found: %s.", String::num_int64(int64_t(env_blend_mode))));1336}1337}13381339if (openxr_api->is_environment_blend_mode_alpha_blend_supported() == OpenXRAPI::OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING) {1340modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);1341}13421343return modes;1344}13451346XRInterface::EnvironmentBlendMode OpenXRInterface::get_environment_blend_mode() const {1347if (openxr_api) {1348XrEnvironmentBlendMode oxr_blend_mode = openxr_api->get_environment_blend_mode();1349switch (oxr_blend_mode) {1350case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: {1351return XR_ENV_BLEND_MODE_OPAQUE;1352} break;1353case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: {1354return XR_ENV_BLEND_MODE_ADDITIVE;1355} break;1356case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: {1357return XR_ENV_BLEND_MODE_ALPHA_BLEND;1358} break;1359default:1360break;1361}1362}13631364return XR_ENV_BLEND_MODE_OPAQUE;1365}13661367bool OpenXRInterface::set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) {1368if (openxr_api) {1369XrEnvironmentBlendMode oxr_blend_mode;1370switch (mode) {1371case XR_ENV_BLEND_MODE_OPAQUE:1372oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;1373break;1374case XR_ENV_BLEND_MODE_ADDITIVE:1375oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;1376break;1377case XR_ENV_BLEND_MODE_ALPHA_BLEND:1378oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;1379break;1380default:1381WARN_PRINT("Unknown blend mode requested: " + String::num_int64(int64_t(mode)));1382oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;1383}1384return openxr_api->set_environment_blend_mode(oxr_blend_mode);1385}1386return false;1387}13881389void OpenXRInterface::on_state_ready() {1390emit_signal(SNAME("session_begun"));1391}13921393void OpenXRInterface::on_state_visible() {1394emit_signal(SNAME("session_visible"));1395}13961397void OpenXRInterface::on_state_synchronized() {1398emit_signal(SNAME("session_synchronized"));1399}14001401void OpenXRInterface::on_state_focused() {1402emit_signal(SNAME("session_focussed"));1403}14041405void OpenXRInterface::on_state_stopping() {1406emit_signal(SNAME("session_stopping"));1407}14081409void OpenXRInterface::on_state_loss_pending() {1410emit_signal(SNAME("session_loss_pending"));1411}14121413void OpenXRInterface::on_state_exiting() {1414emit_signal(SNAME("instance_exiting"));1415}14161417void OpenXRInterface::on_reference_space_change_pending(XrReferenceSpaceType p_type) {1418reference_stage_changing = true;14191420// Emit play area bounds changed signal when the reference space changes.1421PlayAreaMode mode = XR_PLAY_AREA_UNKNOWN;14221423switch (p_type) {1424case XR_REFERENCE_SPACE_TYPE_VIEW:1425mode = XR_PLAY_AREA_3DOF;1426break;1427case XR_REFERENCE_SPACE_TYPE_LOCAL:1428mode = XR_PLAY_AREA_SITTING;1429break;1430case XR_REFERENCE_SPACE_TYPE_STAGE:1431mode = XR_PLAY_AREA_STAGE;1432break;1433case XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR:1434mode = XR_PLAY_AREA_ROOMSCALE;1435break;1436default:1437mode = XR_PLAY_AREA_UNKNOWN;1438break;1439}14401441print_verbose("OpenXR Interface: Play area changed, emitting signal.");1442emit_signal(SNAME("play_area_changed"), mode);1443}14441445void OpenXRInterface::on_refresh_rate_changes(float p_new_rate) {1446emit_signal(SNAME("refresh_rate_changed"), p_new_rate);1447}14481449OpenXRInterface::SessionState OpenXRInterface::get_session_state() {1450if (openxr_api) {1451return (SessionState)openxr_api->get_session_state();1452}14531454return SESSION_STATE_UNKNOWN;1455}14561457/** User Presence. */1458bool OpenXRInterface::is_user_presence_supported() const {1459if (!openxr_api || !openxr_api->is_initialized()) {1460return false;1461} else {1462OpenXRUserPresenceExtension *user_presence_ext = OpenXRUserPresenceExtension::get_singleton();1463return user_presence_ext && user_presence_ext->is_active();1464}1465}14661467bool OpenXRInterface::is_user_present() const {1468// If extension is unavailable or unsupported, we default to user is present.1469if (!is_user_presence_supported()) {1470return true;1471} else {1472OpenXRUserPresenceExtension *user_presence_ext = OpenXRUserPresenceExtension::get_singleton();1473return user_presence_ext->is_user_present();1474}1475}14761477/** Hand tracking. */1478void OpenXRInterface::set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range) {1479ERR_FAIL_INDEX(p_hand, HAND_MAX);1480ERR_FAIL_INDEX(p_motion_range, HAND_MOTION_RANGE_MAX);14811482OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1483if (hand_tracking_ext && hand_tracking_ext->get_active()) {1484XrHandJointsMotionRangeEXT xr_motion_range;1485switch (p_motion_range) {1486case HAND_MOTION_RANGE_UNOBSTRUCTED:1487xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;1488break;1489case HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER:1490xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;1491break;1492default:1493// Shouldn't get here, ERR_FAIL_INDEX should have caught this...1494xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;1495break;1496}14971498hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), xr_motion_range);1499}1500}15011502OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_hand) const {1503ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_MOTION_RANGE_MAX);15041505OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1506if (hand_tracking_ext && hand_tracking_ext->get_active()) {1507XrHandJointsMotionRangeEXT xr_motion_range = hand_tracking_ext->get_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));15081509switch (xr_motion_range) {1510case XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT:1511return HAND_MOTION_RANGE_UNOBSTRUCTED;1512case XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT:1513return HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER;1514default:1515ERR_FAIL_V_MSG(HAND_MOTION_RANGE_MAX, "Unknown motion range returned by OpenXR");1516}1517}15181519return HAND_MOTION_RANGE_MAX;1520}15211522OpenXRInterface::HandTrackedSource OpenXRInterface::get_hand_tracking_source(const Hand p_hand) const {1523ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_TRACKED_SOURCE_UNKNOWN);15241525OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1526if (hand_tracking_ext && hand_tracking_ext->get_active()) {1527OpenXRHandTrackingExtension::HandTrackedSource source = hand_tracking_ext->get_hand_tracking_source(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));1528switch (source) {1529case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNOBSTRUCTED:1530return HAND_TRACKED_SOURCE_UNOBSTRUCTED;1531case OpenXRHandTrackingExtension::OPENXR_SOURCE_CONTROLLER:1532return HAND_TRACKED_SOURCE_CONTROLLER;1533case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNKNOWN:1534case OpenXRHandTrackingExtension::OPENXR_SOURCE_NOT_TRACKED:1535return HAND_TRACKED_SOURCE_UNKNOWN;1536default:1537ERR_FAIL_V_MSG(HAND_TRACKED_SOURCE_UNKNOWN, "Unknown hand tracking source (" + String::num_int64(source) + ") returned by OpenXR");1538}1539}15401541return HAND_TRACKED_SOURCE_UNKNOWN;1542}15431544BitField<OpenXRInterface::HandJointFlags> OpenXRInterface::get_hand_joint_flags(Hand p_hand, HandJoints p_joint) const {1545BitField<OpenXRInterface::HandJointFlags> bits = HAND_JOINT_NONE;15461547OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1548if (hand_tracking_ext && hand_tracking_ext->get_active()) {1549XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1550if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {1551bits.set_flag(HAND_JOINT_ORIENTATION_VALID);1552}1553if (location_flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {1554bits.set_flag(HAND_JOINT_ORIENTATION_TRACKED);1555}1556if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {1557bits.set_flag(HAND_JOINT_POSITION_VALID);1558}1559if (location_flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {1560bits.set_flag(HAND_JOINT_POSITION_TRACKED);1561}15621563XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_velocity_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1564if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {1565bits.set_flag(HAND_JOINT_LINEAR_VELOCITY_VALID);1566}1567if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {1568bits.set_flag(HAND_JOINT_ANGULAR_VELOCITY_VALID);1569}1570}15711572return bits;1573}15741575Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const {1576OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1577if (hand_tracking_ext && hand_tracking_ext->get_active()) {1578return hand_tracking_ext->get_hand_joint_rotation(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1579}15801581return Quaternion();1582}15831584Vector3 OpenXRInterface::get_hand_joint_position(Hand p_hand, HandJoints p_joint) const {1585OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1586if (hand_tracking_ext && hand_tracking_ext->get_active()) {1587return hand_tracking_ext->get_hand_joint_position(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1588}15891590return Vector3();1591}15921593float OpenXRInterface::get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const {1594OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1595if (hand_tracking_ext && hand_tracking_ext->get_active()) {1596return hand_tracking_ext->get_hand_joint_radius(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1597}15981599return 0.0;1600}16011602Vector3 OpenXRInterface::get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const {1603OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1604if (hand_tracking_ext && hand_tracking_ext->get_active()) {1605return hand_tracking_ext->get_hand_joint_linear_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1606}16071608return Vector3();1609}16101611Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const {1612OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1613if (hand_tracking_ext && hand_tracking_ext->get_active()) {1614return hand_tracking_ext->get_hand_joint_angular_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1615}16161617return Vector3();1618}16191620RID OpenXRInterface::get_vrs_texture() {1621if (!openxr_api) {1622return RID();1623}16241625RID density_map = openxr_api->get_density_map_texture();1626if (density_map.is_valid()) {1627return density_map;1628}16291630PackedVector2Array eye_foci;16311632Size2 target_size = get_render_target_size();1633real_t aspect_ratio = target_size.x / target_size.y;1634uint32_t view_count = get_view_count();16351636for (uint32_t v = 0; v < view_count; v++) {1637eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio));1638}16391640xr_vrs.set_vrs_render_region(get_render_region());16411642return xr_vrs.make_vrs_texture(target_size, eye_foci);1643}16441645XRInterface::VRSTextureFormat OpenXRInterface::get_vrs_texture_format() {1646if (!openxr_api) {1647return XR_VRS_TEXTURE_FORMAT_UNIFIED;1648}16491650RID density_map = openxr_api->get_density_map_texture();1651if (density_map.is_valid()) {1652return XR_VRS_TEXTURE_FORMAT_FRAGMENT_DENSITY_MAP;1653}16541655return XR_VRS_TEXTURE_FORMAT_UNIFIED;1656}16571658void OpenXRInterface::set_cpu_level(PerfSettingsLevel p_level) {1659OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();1660if (performance_settings_ext && performance_settings_ext->is_available()) {1661performance_settings_ext->set_cpu_level(p_level);1662}1663}16641665void OpenXRInterface::set_gpu_level(PerfSettingsLevel p_level) {1666OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();1667if (performance_settings_ext && performance_settings_ext->is_available()) {1668performance_settings_ext->set_gpu_level(p_level);1669}1670}16711672void OpenXRInterface::on_cpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {1673emit_signal(SNAME("cpu_level_changed"), p_sub_domain, p_from_level, p_to_level);1674}16751676void OpenXRInterface::on_gpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {1677emit_signal(SNAME("gpu_level_changed"), p_sub_domain, p_from_level, p_to_level);1678}16791680OpenXRInterface::OpenXRInterface() {1681openxr_api = OpenXRAPI::get_singleton();1682if (openxr_api) {1683openxr_api->set_xr_interface(this);1684}16851686// while we don't have head tracking, don't put the headset on the floor...1687_set_default_pos(head_transform, 1.0, 0);1688_set_default_pos(transform_for_view[0], 1.0, 1);1689_set_default_pos(transform_for_view[1], 1.0, 2);1690}16911692OpenXRInterface::~OpenXRInterface() {1693if (is_initialized()) {1694uninitialize();1695}16961697if (openxr_api) {1698openxr_api->set_xr_interface(nullptr);1699openxr_api = nullptr;1700}1701}170217031704