Path: blob/master/modules/openxr/openxr_interface.cpp
10277 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 "servers/rendering/renderer_compositor.h"3940#include <openxr/openxr.h>4142void OpenXRInterface::_bind_methods() {43// lifecycle signals44ADD_SIGNAL(MethodInfo("session_begun"));45ADD_SIGNAL(MethodInfo("session_stopping"));46ADD_SIGNAL(MethodInfo("session_synchronized"));47ADD_SIGNAL(MethodInfo("session_focussed"));48ADD_SIGNAL(MethodInfo("session_visible"));49ADD_SIGNAL(MethodInfo("session_loss_pending"));50ADD_SIGNAL(MethodInfo("instance_exiting"));51ADD_SIGNAL(MethodInfo("pose_recentered"));52ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));5354ADD_SIGNAL(MethodInfo("cpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));55ADD_SIGNAL(MethodInfo("gpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));5657// State58ClassDB::bind_method(D_METHOD("get_session_state"), &OpenXRInterface::get_session_state);5960// Display refresh rate61ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate);62ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate);63ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");6465// Render Target size multiplier66ClassDB::bind_method(D_METHOD("get_render_target_size_multiplier"), &OpenXRInterface::get_render_target_size_multiplier);67ClassDB::bind_method(D_METHOD("set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier);68ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier");6970// Foveation level71ClassDB::bind_method(D_METHOD("is_foveation_supported"), &OpenXRInterface::is_foveation_supported);7273ClassDB::bind_method(D_METHOD("get_foveation_level"), &OpenXRInterface::get_foveation_level);74ClassDB::bind_method(D_METHOD("set_foveation_level", "foveation_level"), &OpenXRInterface::set_foveation_level);75ADD_PROPERTY(PropertyInfo(Variant::INT, "foveation_level"), "set_foveation_level", "get_foveation_level");7677ClassDB::bind_method(D_METHOD("get_foveation_dynamic"), &OpenXRInterface::get_foveation_dynamic);78ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic);79ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic");8081// Action sets82ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active);83ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active);84ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets);8586// Refresh rates87ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);8889// Hand tracking.90ClassDB::bind_method(D_METHOD("set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range);91ClassDB::bind_method(D_METHOD("get_motion_range", "hand"), &OpenXRInterface::get_motion_range);9293ClassDB::bind_method(D_METHOD("get_hand_tracking_source", "hand"), &OpenXRInterface::get_hand_tracking_source);9495ClassDB::bind_method(D_METHOD("get_hand_joint_flags", "hand", "joint"), &OpenXRInterface::get_hand_joint_flags);9697ClassDB::bind_method(D_METHOD("get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation);98ClassDB::bind_method(D_METHOD("get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position);99ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius);100101ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity);102ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);103104ClassDB::bind_method(D_METHOD("is_hand_tracking_supported"), &OpenXRInterface::is_hand_tracking_supported);105ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported);106ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);107108// VRS109ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius);110ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius);111ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength);112ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength);113114// Performance settings.115ClassDB::bind_method(D_METHOD("set_cpu_level", "level"), &OpenXRInterface::set_cpu_level);116ClassDB::bind_method(D_METHOD("set_gpu_level", "level"), &OpenXRInterface::set_gpu_level);117118ADD_GROUP("Vulkan VRS", "vrs_");119ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");120ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");121122BIND_ENUM_CONSTANT(SESSION_STATE_UNKNOWN);123BIND_ENUM_CONSTANT(SESSION_STATE_IDLE);124BIND_ENUM_CONSTANT(SESSION_STATE_READY);125BIND_ENUM_CONSTANT(SESSION_STATE_SYNCHRONIZED);126BIND_ENUM_CONSTANT(SESSION_STATE_VISIBLE);127BIND_ENUM_CONSTANT(SESSION_STATE_FOCUSED);128BIND_ENUM_CONSTANT(SESSION_STATE_STOPPING);129BIND_ENUM_CONSTANT(SESSION_STATE_LOSS_PENDING);130BIND_ENUM_CONSTANT(SESSION_STATE_EXITING);131132BIND_ENUM_CONSTANT(HAND_LEFT);133BIND_ENUM_CONSTANT(HAND_RIGHT);134BIND_ENUM_CONSTANT(HAND_MAX);135136BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_UNOBSTRUCTED);137BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER);138BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_MAX);139140BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNKNOWN);141BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNOBSTRUCTED);142BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_CONTROLLER);143BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_MAX);144145BIND_ENUM_CONSTANT(HAND_JOINT_PALM);146BIND_ENUM_CONSTANT(HAND_JOINT_WRIST);147BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL);148BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PROXIMAL);149BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_DISTAL);150BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP);151BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_METACARPAL);152BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_PROXIMAL);153BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_INTERMEDIATE);154BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_DISTAL);155BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_TIP);156BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_METACARPAL);157BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_PROXIMAL);158BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_INTERMEDIATE);159BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_DISTAL);160BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_TIP);161BIND_ENUM_CONSTANT(HAND_JOINT_RING_METACARPAL);162BIND_ENUM_CONSTANT(HAND_JOINT_RING_PROXIMAL);163BIND_ENUM_CONSTANT(HAND_JOINT_RING_INTERMEDIATE);164BIND_ENUM_CONSTANT(HAND_JOINT_RING_DISTAL);165BIND_ENUM_CONSTANT(HAND_JOINT_RING_TIP);166BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_METACARPAL);167BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_PROXIMAL);168BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_INTERMEDIATE);169BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_DISTAL);170BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP);171BIND_ENUM_CONSTANT(HAND_JOINT_MAX);172173BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_POWER_SAVINGS);174BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_LOW);175BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_HIGH);176BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_BOOST);177178BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_COMPOSITING);179BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_RENDERING);180BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_THERMAL);181182BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_NORMAL);183BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_WARNING);184BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED);185186BIND_BITFIELD_FLAG(HAND_JOINT_NONE);187BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_VALID);188BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_TRACKED);189BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_VALID);190BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_TRACKED);191BIND_BITFIELD_FLAG(HAND_JOINT_LINEAR_VELOCITY_VALID);192BIND_BITFIELD_FLAG(HAND_JOINT_ANGULAR_VELOCITY_VALID);193}194195StringName OpenXRInterface::get_name() const {196return StringName("OpenXR");197}198199uint32_t OpenXRInterface::get_capabilities() const {200return XRInterface::XR_VR + XRInterface::XR_STEREO;201}202203PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {204// These are hardcoded in OpenXR, note that they will only be available if added to our action map205206PackedStringArray arr = {207"head", // XRPositionalTracker for the users head (Mapped from OpenXR /user/head)208"left_hand", // XRControllerTracker for the users left hand (Mapped from OpenXR /user/hand/left)209"right_hand", // XRControllerTracker for the users right hand (Mapped from OpenXR /user/hand/right)210"/user/hand_tracker/left", // XRHandTracker for the users left hand211"/user/hand_tracker/right", // XRHandTracker for the users right hand212"/user/body_tracker", // XRBodyTracker for the users body213"/user/face_tracker", // XRFaceTracker for the users face214"/user/treadmill"215};216217for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {218arr.append_array(wrapper->get_suggested_tracker_names());219}220221return arr;222}223224XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const {225return tracking_state;226}227228void OpenXRInterface::_load_action_map() {229ERR_FAIL_NULL(openxr_api);230231// This may seem a bit duplicitous to a little bit of background info here.232// OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in.233// This gives the user the ability to edit the action map in a UI and customize the actions.234// OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it.235// This system does that push and we store the info needed to then work with this action map going forward.236237// Within our openxr device we maintain a number of classes that wrap the relevant OpenXR objects for this.238// Within OpenXRInterface we have a few internal classes that keep track of what we've created.239// This allow us to process the relevant actions each frame.240241// just in case clean up242free_trackers();243free_interaction_profiles();244free_action_sets();245246Ref<OpenXRActionMap> action_map;247if (Engine::get_singleton()->is_editor_hint()) {248#ifdef TOOLS_ENABLED249action_map.instantiate();250action_map->create_editor_action_sets();251#endif252} else {253String default_tres_name = openxr_api->get_default_action_map_resource_name();254255// Check if we can load our default256if (ResourceLoader::exists(default_tres_name)) {257action_map = ResourceLoader::load(default_tres_name);258}259260// Check if we need to create default action set261if (action_map.is_null()) {262action_map.instantiate();263action_map->create_default_action_sets();264#ifdef TOOLS_ENABLED265// Save our action sets so our user can266action_map->set_path(default_tres_name, true);267ResourceSaver::save(action_map, default_tres_name);268#endif269}270}271272// process our action map273if (action_map.is_valid()) {274HashMap<Ref<OpenXRAction>, Action *> xr_actions;275276Array action_set_array = action_map->get_action_sets();277for (int i = 0; i < action_set_array.size(); i++) {278// Create our action set279Ref<OpenXRActionSet> xr_action_set = action_set_array[i];280ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority());281if (!action_set) {282continue;283}284285// Now create our actions for these286Array actions = xr_action_set->get_actions();287for (int j = 0; j < actions.size(); j++) {288Ref<OpenXRAction> xr_action = actions[j];289290PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();291Vector<Tracker *> trackers_for_action;292293for (int k = 0; k < toplevel_paths.size(); k++) {294// Only check for our tracker if our path is supported.295if (openxr_api->is_top_level_path_supported(toplevel_paths[k])) {296Tracker *tracker = find_tracker(toplevel_paths[k], true);297if (tracker) {298trackers_for_action.push_back(tracker);299}300}301}302303// Only add our action if we have at least one valid toplevel path304if (trackers_for_action.size() > 0) {305Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action);306if (action) {307// add this to our map for creating our interaction profiles308xr_actions[xr_action] = action;309}310}311}312}313314// now do our suggestions315Array interaction_profile_array = action_map->get_interaction_profiles();316for (int i = 0; i < interaction_profile_array.size(); i++) {317Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i];318319// Note, we can only have one entry per interaction profile so if it already exists we clear it out320RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path());321if (ip.is_valid()) {322openxr_api->interaction_profile_clear_bindings(ip);323324for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_interaction_profile->get_binding_modifiers()) {325PackedByteArray bm = xr_binding_modifier->get_ip_modification();326if (!bm.is_empty()) {327openxr_api->interaction_profile_add_modifier(ip, bm);328}329}330331Array xr_bindings = xr_interaction_profile->get_bindings();332for (int j = 0; j < xr_bindings.size(); j++) {333Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];334Ref<OpenXRAction> xr_action = xr_binding->get_action();335336Action *action = nullptr;337if (xr_actions.has(xr_action)) {338action = xr_actions[xr_action];339} else {340print_line("Action ", xr_action->get_name(), " isn't part of an action set!");341continue;342}343344int binding_no = openxr_api->interaction_profile_add_binding(ip, action->action_rid, xr_binding->get_binding_path());345if (binding_no >= 0) {346for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_binding->get_binding_modifiers()) {347// Binding modifiers on bindings can be added to the interaction profile.348PackedByteArray bm = xr_binding_modifier->get_ip_modification();349if (!bm.is_empty()) {350openxr_api->interaction_profile_add_modifier(ip, bm);351}352353// And possibly in the future on the binding itself, we're just preparing for that eventuality.354}355}356}357358// Now submit our suggestions359openxr_api->interaction_profile_suggest_bindings(ip);360361// And record it in our array so we can clean it up later on362if (interaction_profile_array.has(ip)) {363interaction_profile_array.push_back(ip);364}365}366}367}368}369370OpenXRInterface::ActionSet *OpenXRInterface::create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority) {371ERR_FAIL_NULL_V(openxr_api, nullptr);372373// find if it already exists374for (int i = 0; i < action_sets.size(); i++) {375if (action_sets[i]->action_set_name == p_action_set_name) {376// already exists in this set377return nullptr;378}379}380381ActionSet *action_set = memnew(ActionSet);382action_set->action_set_name = p_action_set_name;383action_set->is_active = true;384action_set->action_set_rid = openxr_api->action_set_create(p_action_set_name, p_localized_name, p_priority);385action_sets.push_back(action_set);386387return action_set;388}389390void OpenXRInterface::free_action_sets() {391ERR_FAIL_NULL(openxr_api);392393for (int i = 0; i < action_sets.size(); i++) {394ActionSet *action_set = action_sets[i];395396free_actions(action_set);397398openxr_api->action_set_free(action_set->action_set_rid);399400memfree(action_set);401}402action_sets.clear();403}404405OpenXRInterface::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) {406ERR_FAIL_NULL_V(openxr_api, nullptr);407408for (int i = 0; i < p_action_set->actions.size(); i++) {409if (p_action_set->actions[i]->action_name == p_action_name) {410// already exists in this set411return nullptr;412}413}414415Vector<RID> tracker_rids;416for (int i = 0; i < p_trackers.size(); i++) {417tracker_rids.push_back(p_trackers[i]->tracker_rid);418}419420Action *action = memnew(Action);421if (p_action_type == OpenXRAction::OPENXR_ACTION_POSE) {422// We can't have dual action names in OpenXR hence we added _pose,423// but default, aim and grip and default pose action names in Godot so rename them on the tracker.424// NOTE need to decide on whether we should keep the naming convention or rename it on Godots side425if (p_action_name == "default_pose") {426action->action_name = "default";427} else if (p_action_name == "aim_pose") {428action->action_name = "aim";429} else if (p_action_name == "grip_pose") {430action->action_name = "grip";431} else {432action->action_name = p_action_name;433}434} else {435action->action_name = p_action_name;436}437438action->action_type = p_action_type;439action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids);440p_action_set->actions.push_back(action);441442// we link our actions back to our trackers so we know which actions to check when we're processing our trackers443for (int i = 0; i < p_trackers.size(); i++) {444if (!p_trackers[i]->actions.has(action)) {445p_trackers[i]->actions.push_back(action);446}447}448449return action;450}451452OpenXRInterface::Action *OpenXRInterface::find_action(const String &p_action_name) {453// We just find the first action by this name454455for (int i = 0; i < action_sets.size(); i++) {456for (int j = 0; j < action_sets[i]->actions.size(); j++) {457if (action_sets[i]->actions[j]->action_name == p_action_name) {458return action_sets[i]->actions[j];459}460}461}462463// not found464return nullptr;465}466467void OpenXRInterface::free_actions(ActionSet *p_action_set) {468ERR_FAIL_NULL(openxr_api);469470for (int i = 0; i < p_action_set->actions.size(); i++) {471Action *action = p_action_set->actions[i];472473openxr_api->action_free(action->action_rid);474475memdelete(action);476}477p_action_set->actions.clear();478}479480OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_name, bool p_create) {481XRServer *xr_server = XRServer::get_singleton();482ERR_FAIL_NULL_V(xr_server, nullptr);483ERR_FAIL_NULL_V(openxr_api, nullptr);484485Tracker *tracker = nullptr;486for (int i = 0; i < trackers.size(); i++) {487tracker = trackers[i];488if (tracker->tracker_name == p_tracker_name) {489return tracker;490}491}492493if (!p_create) {494return nullptr;495}496497ERR_FAIL_COND_V(!openxr_api->is_top_level_path_supported(p_tracker_name), nullptr);498499// Create our RID500RID tracker_rid = openxr_api->tracker_create(p_tracker_name);501ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);502503// Create our controller tracker.504Ref<XRControllerTracker> controller_tracker;505controller_tracker.instantiate();506507// We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these.508if (p_tracker_name == "/user/hand/left") {509controller_tracker->set_tracker_name("left_hand");510controller_tracker->set_tracker_desc("Left hand controller");511controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);512} else if (p_tracker_name == "/user/hand/right") {513controller_tracker->set_tracker_name("right_hand");514controller_tracker->set_tracker_desc("Right hand controller");515controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);516} else {517controller_tracker->set_tracker_name(p_tracker_name);518controller_tracker->set_tracker_desc(p_tracker_name);519}520controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);521xr_server->add_tracker(controller_tracker);522523// create a new entry524tracker = memnew(Tracker);525tracker->tracker_name = p_tracker_name;526tracker->tracker_rid = tracker_rid;527tracker->controller_tracker = controller_tracker;528tracker->interaction_profile = RID();529trackers.push_back(tracker);530531return tracker;532}533534void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_profile) {535Tracker *tracker = nullptr;536for (int i = 0; i < trackers.size() && tracker == nullptr; i++) {537if (trackers[i]->tracker_rid == p_tracker) {538tracker = trackers[i];539}540}541ERR_FAIL_NULL(tracker);542543tracker->interaction_profile = p_interaction_profile;544545if (p_interaction_profile.is_null()) {546print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + INTERACTION_PROFILE_NONE);547tracker->controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);548} else {549String name = openxr_api->interaction_profile_get_name(p_interaction_profile);550print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + name);551tracker->controller_tracker->set_tracker_profile(name);552}553}554555void OpenXRInterface::handle_tracker(Tracker *p_tracker) {556ERR_FAIL_NULL(openxr_api);557ERR_FAIL_COND(p_tracker->controller_tracker.is_null());558559// Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction560// profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these561// and rebind them or even offer bindings to controllers that are not known to us.562563// We don't really have a consistent way to detect whether a controller is active however as long as it is564// unbound it seems to be unavailable, so far unknown controller seem to mimic one of the profiles we've565// supplied.566if (p_tracker->interaction_profile.is_null()) {567return;568}569570// We check all actions that are related to our tracker.571for (int i = 0; i < p_tracker->actions.size(); i++) {572Action *action = p_tracker->actions[i];573switch (action->action_type) {574case OpenXRAction::OPENXR_ACTION_BOOL: {575bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid);576p_tracker->controller_tracker->set_input(action->action_name, Variant(pressed));577} break;578case OpenXRAction::OPENXR_ACTION_FLOAT: {579real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid);580p_tracker->controller_tracker->set_input(action->action_name, Variant(value));581} break;582case OpenXRAction::OPENXR_ACTION_VECTOR2: {583Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid);584p_tracker->controller_tracker->set_input(action->action_name, Variant(value));585} break;586case OpenXRAction::OPENXR_ACTION_POSE: {587Transform3D transform;588Vector3 linear, angular;589590XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular);591592if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {593p_tracker->controller_tracker->set_pose(action->action_name, transform, linear, angular, confidence);594} else {595p_tracker->controller_tracker->invalidate_pose(action->action_name);596}597} break;598default: {599// not yet supported600} break;601}602}603}604605void 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) {606ERR_FAIL_NULL(openxr_api);607608Action *action = find_action(p_action_name);609ERR_FAIL_NULL(action);610611// We need to map our tracker name to our OpenXR name for our inbuild names.612String tracker_name = p_tracker_name;613if (tracker_name == "left_hand") {614tracker_name = "/user/hand/left";615} else if (tracker_name == "right_hand") {616tracker_name = "/user/hand/right";617}618Tracker *tracker = find_tracker(tracker_name);619ERR_FAIL_NULL(tracker);620621// TODO OpenXR does not support delay, so we may need to add support for that somehow...622623XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds624625openxr_api->trigger_haptic_pulse(action->action_rid, tracker->tracker_rid, p_frequency, p_amplitude, duration);626}627628void OpenXRInterface::free_trackers() {629XRServer *xr_server = XRServer::get_singleton();630ERR_FAIL_NULL(xr_server);631ERR_FAIL_NULL(openxr_api);632633for (int i = 0; i < trackers.size(); i++) {634Tracker *tracker = trackers[i];635636openxr_api->tracker_free(tracker->tracker_rid);637xr_server->remove_tracker(tracker->controller_tracker);638tracker->controller_tracker.unref();639640memdelete(tracker);641}642trackers.clear();643}644645void OpenXRInterface::free_interaction_profiles() {646ERR_FAIL_NULL(openxr_api);647648for (const RID &interaction_profile : interaction_profiles) {649openxr_api->interaction_profile_free(interaction_profile);650}651interaction_profiles.clear();652}653654bool OpenXRInterface::initialize_on_startup() const {655if (openxr_api == nullptr) {656return false;657} else if (!openxr_api->is_initialized()) {658return false;659} else {660return true;661}662}663664bool OpenXRInterface::is_initialized() const {665return initialized;666}667668bool OpenXRInterface::initialize() {669XRServer *xr_server = XRServer::get_singleton();670ERR_FAIL_NULL_V(xr_server, false);671672if (openxr_api == nullptr) {673return false;674} else if (!openxr_api->is_initialized()) {675return false;676} else if (initialized) {677return true;678}679680// load up our action sets before setting up our session, note that our profiles are suggestions, OpenXR takes ownership of (re)binding681_load_action_map();682683if (!openxr_api->initialize_session()) {684return false;685}686687// we must create a tracker for our head688head.instantiate();689head->set_tracker_type(XRServer::TRACKER_HEAD);690head->set_tracker_name("head");691head->set_tracker_desc("Players head");692xr_server->add_tracker(head);693694// attach action sets695Vector<RID> loaded_action_sets;696for (int i = 0; i < action_sets.size(); i++) {697loaded_action_sets.append(action_sets[i]->action_set_rid);698}699openxr_api->attach_action_sets(loaded_action_sets);700701// make this our primary interface702xr_server->set_primary_interface(this);703704// Register an additional output with the display server, so rendering won't705// be skipped if no windows are visible.706DisplayServer::get_singleton()->register_additional_output(this);707708initialized = true;709710return initialized;711}712713void OpenXRInterface::uninitialize() {714// Our OpenXR driver will clean itself up properly when Godot exits, so we just do some basic stuff here715716// end the session if we need to?717718// cleanup stuff719free_trackers();720free_interaction_profiles();721free_action_sets();722723XRServer *xr_server = XRServer::get_singleton();724if (xr_server) {725if (head.is_valid()) {726xr_server->remove_tracker(head);727head.unref();728}729}730731DisplayServer::get_singleton()->unregister_additional_output(this);732733initialized = false;734}735736Dictionary OpenXRInterface::get_system_info() {737Dictionary dict;738739if (openxr_api) {740dict[SNAME("XRRuntimeName")] = openxr_api->get_runtime_name();741dict[SNAME("XRRuntimeVersion")] = openxr_api->get_runtime_version();742dict[SNAME("OpenXRSystemName")] = openxr_api->get_system_name();743dict[SNAME("OpenXRVendorID")] = openxr_api->get_vendor_id();744}745746return dict;747}748749bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {750if (p_mode == XRInterface::XR_PLAY_AREA_3DOF) {751return false;752}753return true;754}755756XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {757if (!openxr_api || !initialized) {758return XRInterface::XR_PLAY_AREA_UNKNOWN;759}760761XrReferenceSpaceType reference_space = openxr_api->get_reference_space();762763if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL) {764return XRInterface::XR_PLAY_AREA_SITTING;765} else if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT) {766return XRInterface::XR_PLAY_AREA_ROOMSCALE;767} else if (reference_space == XR_REFERENCE_SPACE_TYPE_STAGE) {768return XRInterface::XR_PLAY_AREA_STAGE;769} else if (reference_space == XR_REFERENCE_SPACE_TYPE_MAX_ENUM) {770return XRInterface::XR_PLAY_AREA_CUSTOM;771}772773return XRInterface::XR_PLAY_AREA_UNKNOWN;774}775776bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {777ERR_FAIL_NULL_V(openxr_api, false);778779XrReferenceSpaceType reference_space;780781if (p_mode == XRInterface::XR_PLAY_AREA_SITTING) {782reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;783} else if (p_mode == XRInterface::XR_PLAY_AREA_ROOMSCALE) {784reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;785} else if (p_mode == XRInterface::XR_PLAY_AREA_STAGE) {786reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;787} else {788return false;789}790791if (openxr_api->set_requested_reference_space(reference_space)) {792XRServer *xr_server = XRServer::get_singleton();793if (xr_server) {794xr_server->clear_reference_frame();795}796return true;797}798799return false;800}801802PackedVector3Array OpenXRInterface::get_play_area() const {803XRServer *xr_server = XRServer::get_singleton();804ERR_FAIL_NULL_V(xr_server, PackedVector3Array());805PackedVector3Array arr;806807Vector3 sides[4] = {808Vector3(-0.5f, 0.0f, -0.5f),809Vector3(0.5f, 0.0f, -0.5f),810Vector3(0.5f, 0.0f, 0.5f),811Vector3(-0.5f, 0.0f, 0.5f),812};813814if (openxr_api != nullptr && openxr_api->is_initialized()) {815Size2 extents = openxr_api->get_play_space_bounds();816if (extents.width != 0.0 && extents.height != 0.0) {817Transform3D reference_frame = xr_server->get_reference_frame();818819for (int i = 0; i < 4; i++) {820Vector3 coord = sides[i];821822// Scale it up.823coord.x *= extents.width;824coord.z *= extents.height;825826// Now apply our reference.827Vector3 out = reference_frame.xform(coord);828arr.push_back(out);829}830} else {831WARN_PRINT_ONCE("OpenXR: No extents available.");832}833}834835return arr;836}837838float OpenXRInterface::get_display_refresh_rate() const {839if (openxr_api == nullptr) {840return 0.0;841} else if (!openxr_api->is_initialized()) {842return 0.0;843} else {844return openxr_api->get_display_refresh_rate();845}846}847848void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) {849if (openxr_api == nullptr) {850return;851} else if (!openxr_api->is_initialized()) {852return;853} else {854openxr_api->set_display_refresh_rate(p_refresh_rate);855}856}857858Array OpenXRInterface::get_available_display_refresh_rates() const {859if (openxr_api == nullptr) {860return Array();861} else if (!openxr_api->is_initialized()) {862return Array();863} else {864return openxr_api->get_available_display_refresh_rates();865}866}867868bool OpenXRInterface::is_hand_tracking_supported() {869if (openxr_api == nullptr) {870return false;871} else if (!openxr_api->is_initialized()) {872return false;873} else {874OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();875if (hand_tracking_ext == nullptr) {876return false;877} else {878return hand_tracking_ext->get_active();879}880}881}882883bool OpenXRInterface::is_hand_interaction_supported() const {884if (openxr_api == nullptr) {885return false;886} else if (!openxr_api->is_initialized()) {887return false;888} else {889OpenXRHandInteractionExtension *hand_interaction_ext = OpenXRHandInteractionExtension::get_singleton();890if (hand_interaction_ext == nullptr) {891return false;892} else {893return hand_interaction_ext->is_available();894}895}896}897898bool OpenXRInterface::is_eye_gaze_interaction_supported() {899if (openxr_api == nullptr) {900return false;901} else if (!openxr_api->is_initialized()) {902return false;903} else {904OpenXREyeGazeInteractionExtension *eye_gaze_ext = OpenXREyeGazeInteractionExtension::get_singleton();905if (eye_gaze_ext == nullptr) {906return false;907} else {908return eye_gaze_ext->supports_eye_gaze_interaction();909}910}911}912913bool OpenXRInterface::is_action_set_active(const String &p_action_set) const {914for (ActionSet *action_set : action_sets) {915if (action_set->action_set_name == p_action_set) {916return action_set->is_active;917}918}919920WARN_PRINT("OpenXR: Unknown action set " + p_action_set);921return false;922}923924void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) {925for (ActionSet *action_set : action_sets) {926if (action_set->action_set_name == p_action_set) {927action_set->is_active = p_active;928return;929}930}931932WARN_PRINT("OpenXR: Unknown action set " + p_action_set);933}934935Array OpenXRInterface::get_action_sets() const {936Array arr;937938for (ActionSet *action_set : action_sets) {939arr.push_back(action_set->action_set_name);940}941942return arr;943}944945float OpenXRInterface::get_vrs_min_radius() const {946return xr_vrs.get_vrs_min_radius();947}948949void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) {950xr_vrs.set_vrs_min_radius(p_vrs_min_radius);951}952953float OpenXRInterface::get_vrs_strength() const {954return xr_vrs.get_vrs_strength();955}956957void OpenXRInterface::set_vrs_strength(float p_vrs_strength) {958xr_vrs.set_vrs_strength(p_vrs_strength);959}960961double OpenXRInterface::get_render_target_size_multiplier() const {962if (openxr_api == nullptr) {963return 1.0;964} else {965return openxr_api->get_render_target_size_multiplier();966}967}968969void OpenXRInterface::set_render_target_size_multiplier(double multiplier) {970if (openxr_api == nullptr) {971return;972} else {973openxr_api->set_render_target_size_multiplier(multiplier);974}975}976977bool OpenXRInterface::is_foveation_supported() const {978if (openxr_api == nullptr) {979return false;980} else {981return openxr_api->is_foveation_supported();982}983}984985int OpenXRInterface::get_foveation_level() const {986if (openxr_api == nullptr) {987return 0;988} else {989return openxr_api->get_foveation_level();990}991}992993void OpenXRInterface::set_foveation_level(int p_foveation_level) {994if (openxr_api == nullptr) {995return;996} else {997openxr_api->set_foveation_level(p_foveation_level);998}999}10001001bool OpenXRInterface::get_foveation_dynamic() const {1002if (openxr_api == nullptr) {1003return false;1004} else {1005return openxr_api->get_foveation_dynamic();1006}1007}10081009void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) {1010if (openxr_api == nullptr) {1011return;1012} else {1013openxr_api->set_foveation_dynamic(p_foveation_dynamic);1014}1015}10161017Size2 OpenXRInterface::get_render_target_size() {1018if (openxr_api == nullptr) {1019return Size2();1020} else {1021return openxr_api->get_recommended_target_size();1022}1023}10241025uint32_t OpenXRInterface::get_view_count() {1026// TODO set this based on our configuration1027return 2;1028}10291030void OpenXRInterface::_set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye) {1031p_transform = Transform3D();10321033// if we're not tracking, don't put our head on the floor...1034p_transform.origin.y = 1.5 * p_world_scale;10351036// overkill but..1037if (p_eye == 1) {1038p_transform.origin.x = 0.03 * p_world_scale;1039} else if (p_eye == 2) {1040p_transform.origin.x = -0.03 * p_world_scale;1041}1042}10431044Transform3D OpenXRInterface::get_camera_transform() {1045XRServer *xr_server = XRServer::get_singleton();1046ERR_FAIL_NULL_V(xr_server, Transform3D());10471048Transform3D hmd_transform;1049double world_scale = xr_server->get_world_scale();10501051// head_transform should be updated in process10521053hmd_transform.basis = head_transform.basis;1054hmd_transform.origin = head_transform.origin * world_scale;10551056return hmd_transform;1057}10581059Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {1060XRServer *xr_server = XRServer::get_singleton();1061ERR_FAIL_NULL_V(xr_server, Transform3D());1062ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds.");10631064Transform3D t;1065if (openxr_api && openxr_api->get_view_transform(p_view, t)) {1066// update our cached value if we have a valid transform1067transform_for_view[p_view] = t;1068} else {1069// reuse cached value1070t = transform_for_view[p_view];1071}10721073// Apply our world scale1074double world_scale = xr_server->get_world_scale();1075t.origin *= world_scale;10761077return p_cam_transform * xr_server->get_reference_frame() * t;1078}10791080Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {1081Projection cm;1082ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds.");10831084if (openxr_api) {1085if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {1086return cm;1087}1088}10891090// Failed to get from our OpenXR device? Default to some sort of sensible camera matrix..1091cm.set_for_hmd(p_view + 1, 1.0, 6.0, 14.5, 4.0, 1.5, p_z_near, p_z_far);10921093return cm;1094}10951096Rect2i OpenXRInterface::get_render_region() {1097if (openxr_api) {1098return openxr_api->get_render_region();1099} else {1100return Rect2i();1101}1102}11031104RID OpenXRInterface::get_color_texture() {1105if (openxr_api) {1106return openxr_api->get_color_texture();1107} else {1108return RID();1109}1110}11111112RID OpenXRInterface::get_depth_texture() {1113if (openxr_api) {1114return openxr_api->get_depth_texture();1115} else {1116return RID();1117}1118}11191120RID OpenXRInterface::get_velocity_texture() {1121if (openxr_api) {1122return openxr_api->get_velocity_texture();1123} else {1124return RID();1125}1126}11271128RID OpenXRInterface::get_velocity_depth_texture() {1129if (openxr_api) {1130return openxr_api->get_velocity_depth_texture();1131} else {1132return RID();1133}1134}11351136Size2i OpenXRInterface::get_velocity_target_size() {1137if (openxr_api) {1138return openxr_api->get_velocity_target_size();1139} else {1140return Size2i();1141}1142}11431144void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrackingExtension::HandTrackedHands p_hand) {1145OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1146if (hand_tracking_ext && hand_tracking_ext->get_active()) {1147OpenXRInterface::Tracker *tracker = find_tracker(p_path);1148if (tracker && tracker->controller_tracker.is_valid()) {1149XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);11501151if (location_flags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT)) {1152static 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;1153XRPose::TrackingConfidence confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;1154Transform3D transform;1155Vector3 linear_velocity;1156Vector3 angular_velocity;11571158if ((location_flags & all_location_flags) == all_location_flags) {1159// All flags set? confidence is high!1160confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;1161}11621163if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {1164transform.basis = Basis(hand_tracking_ext->get_hand_joint_rotation(p_hand, XR_HAND_JOINT_PALM_EXT));1165}1166if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {1167transform.origin = hand_tracking_ext->get_hand_joint_position(p_hand, XR_HAND_JOINT_PALM_EXT);1168}11691170XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);1171if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {1172linear_velocity = hand_tracking_ext->get_hand_joint_linear_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);1173}1174if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {1175angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);1176}11771178tracker->controller_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);1179} else {1180tracker->controller_tracker->invalidate_pose("skeleton");1181}1182}1183}1184}11851186void OpenXRInterface::process() {1187if (openxr_api) {1188// do our normal process1189if (openxr_api->process()) {1190Transform3D t;1191Vector3 linear_velocity;1192Vector3 angular_velocity;1193head_confidence = openxr_api->get_head_center(t, linear_velocity, angular_velocity);1194if (head_confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {1195// Only update our transform if we have one to update it with1196// note that poses are stored without world scale and reference frame applied!1197head_transform = t;1198head_linear_velocity = linear_velocity;1199head_angular_velocity = angular_velocity;1200}1201}12021203// handle our action sets....1204Vector<RID> active_sets;1205for (int i = 0; i < action_sets.size(); i++) {1206if (action_sets[i]->is_active) {1207active_sets.push_back(action_sets[i]->action_set_rid);1208}1209}12101211if (openxr_api->sync_action_sets(active_sets)) {1212for (int i = 0; i < trackers.size(); i++) {1213handle_tracker(trackers[i]);1214}1215}12161217// Handle hand tracking1218handle_hand_tracking("/user/hand/left", OpenXRHandTrackingExtension::OPENXR_TRACKED_LEFT_HAND);1219handle_hand_tracking("/user/hand/right", OpenXRHandTrackingExtension::OPENXR_TRACKED_RIGHT_HAND);1220}12211222if (head.is_valid()) {1223head->set_pose("default", head_transform, head_linear_velocity, head_angular_velocity, head_confidence);1224}12251226if (reference_stage_changing) {1227// Now that we have updated tracking information in our updated reference space, trigger our pose recentered signal.1228emit_signal(SNAME("pose_recentered"));1229reference_stage_changing = false;1230}1231}12321233void OpenXRInterface::pre_render() {1234if (openxr_api) {1235openxr_api->pre_render();1236}1237}12381239bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {1240if (openxr_api) {1241return openxr_api->pre_draw_viewport(p_render_target);1242} else {1243// don't render1244return false;1245}1246}12471248Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {1249Vector<BlitToScreen> blit_to_screen;12501251#ifndef ANDROID_ENABLED1252// If separate HMD we should output one eye to screen1253if (p_screen_rect != Rect2()) {1254BlitToScreen blit;12551256blit.render_target = p_render_target;1257blit.multi_view.use_layer = true;1258blit.multi_view.layer = 0;1259blit.lens_distortion.apply = false;12601261Size2 render_size = get_render_target_size();1262Rect2 dst_rect = p_screen_rect;1263float new_height = dst_rect.size.x * (render_size.y / render_size.x);1264if (new_height > dst_rect.size.y) {1265dst_rect.position.y = (0.5 * dst_rect.size.y) - (0.5 * new_height);1266dst_rect.size.y = new_height;1267} else {1268float new_width = dst_rect.size.y * (render_size.x / render_size.y);12691270dst_rect.position.x = (0.5 * dst_rect.size.x) - (0.5 * new_width);1271dst_rect.size.x = new_width;1272}12731274blit.dst_rect = dst_rect;1275blit_to_screen.push_back(blit);1276}1277#endif12781279if (openxr_api) {1280openxr_api->post_draw_viewport(p_render_target);1281}12821283return blit_to_screen;1284}12851286void OpenXRInterface::end_frame() {1287if (openxr_api) {1288openxr_api->end_frame();1289}1290}12911292bool OpenXRInterface::is_passthrough_supported() {1293return get_supported_environment_blend_modes().find(XR_ENV_BLEND_MODE_ALPHA_BLEND);1294}12951296bool OpenXRInterface::is_passthrough_enabled() {1297return get_environment_blend_mode() == XR_ENV_BLEND_MODE_ALPHA_BLEND;1298}12991300bool OpenXRInterface::start_passthrough() {1301return set_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND);1302}13031304void OpenXRInterface::stop_passthrough() {1305set_environment_blend_mode(XR_ENV_BLEND_MODE_OPAQUE);1306}13071308Array OpenXRInterface::get_supported_environment_blend_modes() {1309Array modes;13101311if (!openxr_api) {1312return modes;1313}13141315const Vector<XrEnvironmentBlendMode> env_blend_modes = openxr_api->get_supported_environment_blend_modes();13161317for (const XrEnvironmentBlendMode &env_blend_mode : env_blend_modes) {1318switch (env_blend_mode) {1319case XR_ENVIRONMENT_BLEND_MODE_OPAQUE:1320modes.push_back(XR_ENV_BLEND_MODE_OPAQUE);1321break;1322case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE:1323modes.push_back(XR_ENV_BLEND_MODE_ADDITIVE);1324break;1325case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND:1326modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);1327break;1328default:1329WARN_PRINT(vformat("Unsupported blend mode found: %s.", String::num_int64(int64_t(env_blend_mode))));1330}1331}13321333if (openxr_api->is_environment_blend_mode_alpha_blend_supported() == OpenXRAPI::OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING) {1334modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);1335}13361337return modes;1338}13391340XRInterface::EnvironmentBlendMode OpenXRInterface::get_environment_blend_mode() const {1341if (openxr_api) {1342XrEnvironmentBlendMode oxr_blend_mode = openxr_api->get_environment_blend_mode();1343switch (oxr_blend_mode) {1344case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: {1345return XR_ENV_BLEND_MODE_OPAQUE;1346} break;1347case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: {1348return XR_ENV_BLEND_MODE_ADDITIVE;1349} break;1350case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: {1351return XR_ENV_BLEND_MODE_ALPHA_BLEND;1352} break;1353default:1354break;1355}1356}13571358return XR_ENV_BLEND_MODE_OPAQUE;1359}13601361bool OpenXRInterface::set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) {1362if (openxr_api) {1363XrEnvironmentBlendMode oxr_blend_mode;1364switch (mode) {1365case XR_ENV_BLEND_MODE_OPAQUE:1366oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;1367break;1368case XR_ENV_BLEND_MODE_ADDITIVE:1369oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;1370break;1371case XR_ENV_BLEND_MODE_ALPHA_BLEND:1372oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;1373break;1374default:1375WARN_PRINT("Unknown blend mode requested: " + String::num_int64(int64_t(mode)));1376oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;1377}1378return openxr_api->set_environment_blend_mode(oxr_blend_mode);1379}1380return false;1381}13821383void OpenXRInterface::on_state_ready() {1384emit_signal(SNAME("session_begun"));1385}13861387void OpenXRInterface::on_state_visible() {1388emit_signal(SNAME("session_visible"));1389}13901391void OpenXRInterface::on_state_synchronized() {1392emit_signal(SNAME("session_synchronized"));1393}13941395void OpenXRInterface::on_state_focused() {1396emit_signal(SNAME("session_focussed"));1397}13981399void OpenXRInterface::on_state_stopping() {1400emit_signal(SNAME("session_stopping"));1401}14021403void OpenXRInterface::on_state_loss_pending() {1404emit_signal(SNAME("session_loss_pending"));1405}14061407void OpenXRInterface::on_state_exiting() {1408emit_signal(SNAME("instance_exiting"));1409}14101411void OpenXRInterface::on_reference_space_change_pending() {1412reference_stage_changing = true;1413}14141415void OpenXRInterface::on_refresh_rate_changes(float p_new_rate) {1416emit_signal(SNAME("refresh_rate_changed"), p_new_rate);1417}14181419OpenXRInterface::SessionState OpenXRInterface::get_session_state() {1420if (openxr_api) {1421return (SessionState)openxr_api->get_session_state();1422}14231424return SESSION_STATE_UNKNOWN;1425}14261427/** Hand tracking. */1428void OpenXRInterface::set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range) {1429ERR_FAIL_INDEX(p_hand, HAND_MAX);1430ERR_FAIL_INDEX(p_motion_range, HAND_MOTION_RANGE_MAX);14311432OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1433if (hand_tracking_ext && hand_tracking_ext->get_active()) {1434XrHandJointsMotionRangeEXT xr_motion_range;1435switch (p_motion_range) {1436case HAND_MOTION_RANGE_UNOBSTRUCTED:1437xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;1438break;1439case HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER:1440xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;1441break;1442default:1443// Shouldn't get here, ERR_FAIL_INDEX should have caught this...1444xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;1445break;1446}14471448hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), xr_motion_range);1449}1450}14511452OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_hand) const {1453ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_MOTION_RANGE_MAX);14541455OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1456if (hand_tracking_ext && hand_tracking_ext->get_active()) {1457XrHandJointsMotionRangeEXT xr_motion_range = hand_tracking_ext->get_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));14581459switch (xr_motion_range) {1460case XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT:1461return HAND_MOTION_RANGE_UNOBSTRUCTED;1462case XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT:1463return HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER;1464default:1465ERR_FAIL_V_MSG(HAND_MOTION_RANGE_MAX, "Unknown motion range returned by OpenXR");1466}1467}14681469return HAND_MOTION_RANGE_MAX;1470}14711472OpenXRInterface::HandTrackedSource OpenXRInterface::get_hand_tracking_source(const Hand p_hand) const {1473ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_TRACKED_SOURCE_UNKNOWN);14741475OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1476if (hand_tracking_ext && hand_tracking_ext->get_active()) {1477OpenXRHandTrackingExtension::HandTrackedSource source = hand_tracking_ext->get_hand_tracking_source(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));1478switch (source) {1479case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNOBSTRUCTED:1480return HAND_TRACKED_SOURCE_UNOBSTRUCTED;1481case OpenXRHandTrackingExtension::OPENXR_SOURCE_CONTROLLER:1482return HAND_TRACKED_SOURCE_CONTROLLER;1483case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNKNOWN:1484case OpenXRHandTrackingExtension::OPENXR_SOURCE_NOT_TRACKED:1485return HAND_TRACKED_SOURCE_UNKNOWN;1486default:1487ERR_FAIL_V_MSG(HAND_TRACKED_SOURCE_UNKNOWN, "Unknown hand tracking source (" + String::num_int64(source) + ") returned by OpenXR");1488}1489}14901491return HAND_TRACKED_SOURCE_UNKNOWN;1492}14931494BitField<OpenXRInterface::HandJointFlags> OpenXRInterface::get_hand_joint_flags(Hand p_hand, HandJoints p_joint) const {1495BitField<OpenXRInterface::HandJointFlags> bits = HAND_JOINT_NONE;14961497OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1498if (hand_tracking_ext && hand_tracking_ext->get_active()) {1499XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1500if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {1501bits.set_flag(HAND_JOINT_ORIENTATION_VALID);1502}1503if (location_flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {1504bits.set_flag(HAND_JOINT_ORIENTATION_TRACKED);1505}1506if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {1507bits.set_flag(HAND_JOINT_POSITION_VALID);1508}1509if (location_flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {1510bits.set_flag(HAND_JOINT_POSITION_TRACKED);1511}15121513XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_velocity_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1514if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {1515bits.set_flag(HAND_JOINT_LINEAR_VELOCITY_VALID);1516}1517if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {1518bits.set_flag(HAND_JOINT_ANGULAR_VELOCITY_VALID);1519}1520}15211522return bits;1523}15241525Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const {1526OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1527if (hand_tracking_ext && hand_tracking_ext->get_active()) {1528return hand_tracking_ext->get_hand_joint_rotation(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1529}15301531return Quaternion();1532}15331534Vector3 OpenXRInterface::get_hand_joint_position(Hand p_hand, HandJoints p_joint) const {1535OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1536if (hand_tracking_ext && hand_tracking_ext->get_active()) {1537return hand_tracking_ext->get_hand_joint_position(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1538}15391540return Vector3();1541}15421543float OpenXRInterface::get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const {1544OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1545if (hand_tracking_ext && hand_tracking_ext->get_active()) {1546return hand_tracking_ext->get_hand_joint_radius(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1547}15481549return 0.0;1550}15511552Vector3 OpenXRInterface::get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const {1553OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1554if (hand_tracking_ext && hand_tracking_ext->get_active()) {1555return hand_tracking_ext->get_hand_joint_linear_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1556}15571558return Vector3();1559}15601561Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const {1562OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();1563if (hand_tracking_ext && hand_tracking_ext->get_active()) {1564return hand_tracking_ext->get_hand_joint_angular_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));1565}15661567return Vector3();1568}15691570RID OpenXRInterface::get_vrs_texture() {1571if (!openxr_api) {1572return RID();1573}15741575RID density_map = openxr_api->get_density_map_texture();1576if (density_map.is_valid()) {1577return density_map;1578}15791580PackedVector2Array eye_foci;15811582Size2 target_size = get_render_target_size();1583real_t aspect_ratio = target_size.x / target_size.y;1584uint32_t view_count = get_view_count();15851586for (uint32_t v = 0; v < view_count; v++) {1587eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio));1588}15891590xr_vrs.set_vrs_render_region(get_render_region());15911592return xr_vrs.make_vrs_texture(target_size, eye_foci);1593}15941595XRInterface::VRSTextureFormat OpenXRInterface::get_vrs_texture_format() {1596if (!openxr_api) {1597return XR_VRS_TEXTURE_FORMAT_UNIFIED;1598}15991600RID density_map = openxr_api->get_density_map_texture();1601if (density_map.is_valid()) {1602return XR_VRS_TEXTURE_FORMAT_FRAGMENT_DENSITY_MAP;1603}16041605return XR_VRS_TEXTURE_FORMAT_UNIFIED;1606}16071608void OpenXRInterface::set_cpu_level(PerfSettingsLevel p_level) {1609OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();1610if (performance_settings_ext && performance_settings_ext->is_available()) {1611performance_settings_ext->set_cpu_level(p_level);1612}1613}16141615void OpenXRInterface::set_gpu_level(PerfSettingsLevel p_level) {1616OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();1617if (performance_settings_ext && performance_settings_ext->is_available()) {1618performance_settings_ext->set_gpu_level(p_level);1619}1620}16211622void OpenXRInterface::on_cpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {1623emit_signal(SNAME("cpu_level_changed"), p_sub_domain, p_from_level, p_to_level);1624}16251626void OpenXRInterface::on_gpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {1627emit_signal(SNAME("gpu_level_changed"), p_sub_domain, p_from_level, p_to_level);1628}16291630OpenXRInterface::OpenXRInterface() {1631openxr_api = OpenXRAPI::get_singleton();1632if (openxr_api) {1633openxr_api->set_xr_interface(this);1634}16351636// while we don't have head tracking, don't put the headset on the floor...1637_set_default_pos(head_transform, 1.0, 0);1638_set_default_pos(transform_for_view[0], 1.0, 1);1639_set_default_pos(transform_for_view[1], 1.0, 2);1640}16411642OpenXRInterface::~OpenXRInterface() {1643if (is_initialized()) {1644uninitialize();1645}16461647if (openxr_api) {1648openxr_api->set_xr_interface(nullptr);1649openxr_api = nullptr;1650}1651}165216531654