| 1 | /**************************************************************************/ | 
|---|
| 2 | /*  openxr_interface.cpp                                                  */ | 
|---|
| 3 | /**************************************************************************/ | 
|---|
| 4 | /*                         This file is part of:                          */ | 
|---|
| 5 | /*                             GODOT ENGINE                               */ | 
|---|
| 6 | /*                        https://godotengine.org                         */ | 
|---|
| 7 | /**************************************************************************/ | 
|---|
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
|---|
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
|---|
| 10 | /*                                                                        */ | 
|---|
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining  */ | 
|---|
| 12 | /* a copy of this software and associated documentation files (the        */ | 
|---|
| 13 | /* "Software"), to deal in the Software without restriction, including    */ | 
|---|
| 14 | /* without limitation the rights to use, copy, modify, merge, publish,    */ | 
|---|
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | 
|---|
| 16 | /* permit persons to whom the Software is furnished to do so, subject to  */ | 
|---|
| 17 | /* the following conditions:                                              */ | 
|---|
| 18 | /*                                                                        */ | 
|---|
| 19 | /* The above copyright notice and this permission notice shall be         */ | 
|---|
| 20 | /* included in all copies or substantial portions of the Software.        */ | 
|---|
| 21 | /*                                                                        */ | 
|---|
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | 
|---|
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | 
|---|
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | 
|---|
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | 
|---|
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | 
|---|
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | 
|---|
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | 
|---|
| 29 | /**************************************************************************/ | 
|---|
| 30 |  | 
|---|
| 31 | #include "openxr_interface.h" | 
|---|
| 32 |  | 
|---|
| 33 | #include "core/io/resource_loader.h" | 
|---|
| 34 | #include "core/io/resource_saver.h" | 
|---|
| 35 | #include "servers/rendering/rendering_server_globals.h" | 
|---|
| 36 |  | 
|---|
| 37 | #include "extensions/openxr_hand_tracking_extension.h" | 
|---|
| 38 |  | 
|---|
| 39 | void OpenXRInterface::_bind_methods() { | 
|---|
| 40 | // lifecycle signals | 
|---|
| 41 | ADD_SIGNAL(MethodInfo( "session_begun")); | 
|---|
| 42 | ADD_SIGNAL(MethodInfo( "session_stopping")); | 
|---|
| 43 | ADD_SIGNAL(MethodInfo( "session_focussed")); | 
|---|
| 44 | ADD_SIGNAL(MethodInfo( "session_visible")); | 
|---|
| 45 | ADD_SIGNAL(MethodInfo( "pose_recentered")); | 
|---|
| 46 |  | 
|---|
| 47 | // Display refresh rate | 
|---|
| 48 | ClassDB::bind_method(D_METHOD( "get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate); | 
|---|
| 49 | ClassDB::bind_method(D_METHOD( "set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); | 
|---|
| 50 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); | 
|---|
| 51 |  | 
|---|
| 52 | // Render Target size multiplier | 
|---|
| 53 | ClassDB::bind_method(D_METHOD( "get_render_target_size_multiplier"), &OpenXRInterface::get_render_target_size_multiplier); | 
|---|
| 54 | ClassDB::bind_method(D_METHOD( "set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier); | 
|---|
| 55 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier"); | 
|---|
| 56 |  | 
|---|
| 57 | ClassDB::bind_method(D_METHOD( "is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); | 
|---|
| 58 | ClassDB::bind_method(D_METHOD( "set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); | 
|---|
| 59 | ClassDB::bind_method(D_METHOD( "get_action_sets"), &OpenXRInterface::get_action_sets); | 
|---|
| 60 |  | 
|---|
| 61 | ClassDB::bind_method(D_METHOD( "get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); | 
|---|
| 62 |  | 
|---|
| 63 | // Hand tracking. | 
|---|
| 64 | ClassDB::bind_method(D_METHOD( "set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range); | 
|---|
| 65 | ClassDB::bind_method(D_METHOD( "get_motion_range", "hand"), &OpenXRInterface::get_motion_range); | 
|---|
| 66 |  | 
|---|
| 67 | ClassDB::bind_method(D_METHOD( "get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation); | 
|---|
| 68 | ClassDB::bind_method(D_METHOD( "get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position); | 
|---|
| 69 | ClassDB::bind_method(D_METHOD( "get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius); | 
|---|
| 70 |  | 
|---|
| 71 | ClassDB::bind_method(D_METHOD( "get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity); | 
|---|
| 72 | ClassDB::bind_method(D_METHOD( "get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity); | 
|---|
| 73 |  | 
|---|
| 74 | BIND_ENUM_CONSTANT(HAND_LEFT); | 
|---|
| 75 | BIND_ENUM_CONSTANT(HAND_RIGHT); | 
|---|
| 76 | BIND_ENUM_CONSTANT(HAND_MAX); | 
|---|
| 77 |  | 
|---|
| 78 | BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_UNOBSTRUCTED); | 
|---|
| 79 | BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER); | 
|---|
| 80 | BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_MAX); | 
|---|
| 81 |  | 
|---|
| 82 | BIND_ENUM_CONSTANT(HAND_JOINT_PALM); | 
|---|
| 83 | BIND_ENUM_CONSTANT(HAND_JOINT_WRIST); | 
|---|
| 84 | BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL); | 
|---|
| 85 | BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PROXIMAL); | 
|---|
| 86 | BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_DISTAL); | 
|---|
| 87 | BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP); | 
|---|
| 88 | BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_METACARPAL); | 
|---|
| 89 | BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_PROXIMAL); | 
|---|
| 90 | BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_INTERMEDIATE); | 
|---|
| 91 | BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_DISTAL); | 
|---|
| 92 | BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_TIP); | 
|---|
| 93 | BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_METACARPAL); | 
|---|
| 94 | BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_PROXIMAL); | 
|---|
| 95 | BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_INTERMEDIATE); | 
|---|
| 96 | BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_DISTAL); | 
|---|
| 97 | BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_TIP); | 
|---|
| 98 | BIND_ENUM_CONSTANT(HAND_JOINT_RING_METACARPAL); | 
|---|
| 99 | BIND_ENUM_CONSTANT(HAND_JOINT_RING_PROXIMAL); | 
|---|
| 100 | BIND_ENUM_CONSTANT(HAND_JOINT_RING_INTERMEDIATE); | 
|---|
| 101 | BIND_ENUM_CONSTANT(HAND_JOINT_RING_DISTAL); | 
|---|
| 102 | BIND_ENUM_CONSTANT(HAND_JOINT_RING_TIP); | 
|---|
| 103 | BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_METACARPAL); | 
|---|
| 104 | BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_PROXIMAL); | 
|---|
| 105 | BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_INTERMEDIATE); | 
|---|
| 106 | BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_DISTAL); | 
|---|
| 107 | BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP); | 
|---|
| 108 | BIND_ENUM_CONSTANT(HAND_JOINT_MAX); | 
|---|
| 109 | } | 
|---|
| 110 |  | 
|---|
| 111 | StringName OpenXRInterface::get_name() const { | 
|---|
| 112 | return StringName( "OpenXR"); | 
|---|
| 113 | }; | 
|---|
| 114 |  | 
|---|
| 115 | uint32_t OpenXRInterface::get_capabilities() const { | 
|---|
| 116 | return XRInterface::XR_VR + XRInterface::XR_STEREO; | 
|---|
| 117 | }; | 
|---|
| 118 |  | 
|---|
| 119 | PackedStringArray OpenXRInterface::get_suggested_tracker_names() const { | 
|---|
| 120 | // These are hardcoded in OpenXR, note that they will only be available if added to our action map | 
|---|
| 121 |  | 
|---|
| 122 | PackedStringArray arr = { | 
|---|
| 123 | "left_hand", // /user/hand/left is mapped to our defaults | 
|---|
| 124 | "right_hand", // /user/hand/right is mapped to our defaults | 
|---|
| 125 | "/user/treadmill", | 
|---|
| 126 |  | 
|---|
| 127 | // Even though these are only available if you have the tracker extension, | 
|---|
| 128 | // we add these as we may be deploying on a different platform than our | 
|---|
| 129 | // editor is running on. | 
|---|
| 130 | "/user/vive_tracker_htcx/role/handheld_object", | 
|---|
| 131 | "/user/vive_tracker_htcx/role/left_foot", | 
|---|
| 132 | "/user/vive_tracker_htcx/role/right_foot", | 
|---|
| 133 | "/user/vive_tracker_htcx/role/left_shoulder", | 
|---|
| 134 | "/user/vive_tracker_htcx/role/right_shoulder", | 
|---|
| 135 | "/user/vive_tracker_htcx/role/left_elbow", | 
|---|
| 136 | "/user/vive_tracker_htcx/role/right_elbow", | 
|---|
| 137 | "/user/vive_tracker_htcx/role/left_knee", | 
|---|
| 138 | "/user/vive_tracker_htcx/role/right_knee", | 
|---|
| 139 | "/user/vive_tracker_htcx/role/waist", | 
|---|
| 140 | "/user/vive_tracker_htcx/role/chest", | 
|---|
| 141 | "/user/vive_tracker_htcx/role/camera", | 
|---|
| 142 | "/user/vive_tracker_htcx/role/keyboard" | 
|---|
| 143 | }; | 
|---|
| 144 |  | 
|---|
| 145 | return arr; | 
|---|
| 146 | } | 
|---|
| 147 |  | 
|---|
| 148 | XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const { | 
|---|
| 149 | return tracking_state; | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | void OpenXRInterface::_load_action_map() { | 
|---|
| 153 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 154 |  | 
|---|
| 155 | // This may seem a bit duplicitous to a little bit of background info here. | 
|---|
| 156 | // OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in. | 
|---|
| 157 | // This gives the user the ability to edit the action map in a UI and customize the actions. | 
|---|
| 158 | // OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it. | 
|---|
| 159 | // This system does that push and we store the info needed to then work with this action map going forward. | 
|---|
| 160 |  | 
|---|
| 161 | // Within our openxr device we maintain a number of classes that wrap the relevant OpenXR objects for this. | 
|---|
| 162 | // Within OpenXRInterface we have a few internal classes that keep track of what we've created. | 
|---|
| 163 | // This allow us to process the relevant actions each frame. | 
|---|
| 164 |  | 
|---|
| 165 | // just in case clean up | 
|---|
| 166 | free_trackers(); | 
|---|
| 167 | free_interaction_profiles(); | 
|---|
| 168 | free_action_sets(); | 
|---|
| 169 |  | 
|---|
| 170 | Ref<OpenXRActionMap> action_map; | 
|---|
| 171 | if (Engine::get_singleton()->is_editor_hint()) { | 
|---|
| 172 | #ifdef TOOLS_ENABLED | 
|---|
| 173 | action_map.instantiate(); | 
|---|
| 174 | action_map->create_editor_action_sets(); | 
|---|
| 175 | #endif | 
|---|
| 176 | } else { | 
|---|
| 177 | String default_tres_name = openxr_api->get_default_action_map_resource_name(); | 
|---|
| 178 |  | 
|---|
| 179 | // Check if we can load our default | 
|---|
| 180 | if (ResourceLoader::exists(default_tres_name)) { | 
|---|
| 181 | action_map = ResourceLoader::load(default_tres_name); | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | // Check if we need to create default action set | 
|---|
| 185 | if (action_map.is_null()) { | 
|---|
| 186 | action_map.instantiate(); | 
|---|
| 187 | action_map->create_default_action_sets(); | 
|---|
| 188 | #ifdef TOOLS_ENABLED | 
|---|
| 189 | // Save our action sets so our user can | 
|---|
| 190 | action_map->set_path(default_tres_name, true); | 
|---|
| 191 | ResourceSaver::save(action_map, default_tres_name); | 
|---|
| 192 | #endif | 
|---|
| 193 | } | 
|---|
| 194 | } | 
|---|
| 195 |  | 
|---|
| 196 | // process our action map | 
|---|
| 197 | if (action_map.is_valid()) { | 
|---|
| 198 | HashMap<Ref<OpenXRAction>, Action *> xr_actions; | 
|---|
| 199 |  | 
|---|
| 200 | Array action_set_array = action_map->get_action_sets(); | 
|---|
| 201 | for (int i = 0; i < action_set_array.size(); i++) { | 
|---|
| 202 | // Create our action set | 
|---|
| 203 | Ref<OpenXRActionSet> xr_action_set = action_set_array[i]; | 
|---|
| 204 | ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority()); | 
|---|
| 205 | if (!action_set) { | 
|---|
| 206 | continue; | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | // Now create our actions for these | 
|---|
| 210 | Array actions = xr_action_set->get_actions(); | 
|---|
| 211 | for (int j = 0; j < actions.size(); j++) { | 
|---|
| 212 | Ref<OpenXRAction> xr_action = actions[j]; | 
|---|
| 213 |  | 
|---|
| 214 | PackedStringArray toplevel_paths = xr_action->get_toplevel_paths(); | 
|---|
| 215 | Vector<Tracker *> trackers_for_action; | 
|---|
| 216 |  | 
|---|
| 217 | for (int k = 0; k < toplevel_paths.size(); k++) { | 
|---|
| 218 | // Only check for our tracker if our path is supported. | 
|---|
| 219 | if (openxr_api->is_top_level_path_supported(toplevel_paths[k])) { | 
|---|
| 220 | Tracker *tracker = find_tracker(toplevel_paths[k], true); | 
|---|
| 221 | if (tracker) { | 
|---|
| 222 | trackers_for_action.push_back(tracker); | 
|---|
| 223 | } | 
|---|
| 224 | } | 
|---|
| 225 | } | 
|---|
| 226 |  | 
|---|
| 227 | // Only add our action if we have at least one valid toplevel path | 
|---|
| 228 | if (trackers_for_action.size() > 0) { | 
|---|
| 229 | Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action); | 
|---|
| 230 | if (action) { | 
|---|
| 231 | // add this to our map for creating our interaction profiles | 
|---|
| 232 | xr_actions[xr_action] = action; | 
|---|
| 233 | } | 
|---|
| 234 | } | 
|---|
| 235 | } | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | // now do our suggestions | 
|---|
| 239 | Array interaction_profile_array = action_map->get_interaction_profiles(); | 
|---|
| 240 | for (int i = 0; i < interaction_profile_array.size(); i++) { | 
|---|
| 241 | Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i]; | 
|---|
| 242 |  | 
|---|
| 243 | // Note, we can only have one entry per interaction profile so if it already exists we clear it out | 
|---|
| 244 | RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path()); | 
|---|
| 245 | if (ip.is_valid()) { | 
|---|
| 246 | openxr_api->interaction_profile_clear_bindings(ip); | 
|---|
| 247 |  | 
|---|
| 248 | Array xr_bindings = xr_interaction_profile->get_bindings(); | 
|---|
| 249 | for (int j = 0; j < xr_bindings.size(); j++) { | 
|---|
| 250 | Ref<OpenXRIPBinding> xr_binding = xr_bindings[j]; | 
|---|
| 251 | Ref<OpenXRAction> xr_action = xr_binding->get_action(); | 
|---|
| 252 |  | 
|---|
| 253 | Action *action = nullptr; | 
|---|
| 254 | if (xr_actions.has(xr_action)) { | 
|---|
| 255 | action = xr_actions[xr_action]; | 
|---|
| 256 | } else { | 
|---|
| 257 | print_line( "Action ", xr_action->get_name(), " isn't part of an action set!"); | 
|---|
| 258 | continue; | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|
| 261 | PackedStringArray paths = xr_binding->get_paths(); | 
|---|
| 262 | for (int k = 0; k < paths.size(); k++) { | 
|---|
| 263 | openxr_api->interaction_profile_add_binding(ip, action->action_rid, paths[k]); | 
|---|
| 264 | } | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | // Now submit our suggestions | 
|---|
| 268 | openxr_api->interaction_profile_suggest_bindings(ip); | 
|---|
| 269 |  | 
|---|
| 270 | // And record it in our array so we can clean it up later on | 
|---|
| 271 | if (interaction_profile_array.has(ip)) { | 
|---|
| 272 | interaction_profile_array.push_back(ip); | 
|---|
| 273 | } | 
|---|
| 274 | } | 
|---|
| 275 | } | 
|---|
| 276 | } | 
|---|
| 277 | } | 
|---|
| 278 |  | 
|---|
| 279 | OpenXRInterface::ActionSet *OpenXRInterface::create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority) { | 
|---|
| 280 | ERR_FAIL_NULL_V(openxr_api, nullptr); | 
|---|
| 281 |  | 
|---|
| 282 | // find if it already exists | 
|---|
| 283 | for (int i = 0; i < action_sets.size(); i++) { | 
|---|
| 284 | if (action_sets[i]->action_set_name == p_action_set_name) { | 
|---|
| 285 | // already exists in this set | 
|---|
| 286 | return nullptr; | 
|---|
| 287 | } | 
|---|
| 288 | } | 
|---|
| 289 |  | 
|---|
| 290 | ActionSet *action_set = memnew(ActionSet); | 
|---|
| 291 | action_set->action_set_name = p_action_set_name; | 
|---|
| 292 | action_set->is_active = true; | 
|---|
| 293 | action_set->action_set_rid = openxr_api->action_set_create(p_action_set_name, p_localized_name, p_priority); | 
|---|
| 294 | action_sets.push_back(action_set); | 
|---|
| 295 |  | 
|---|
| 296 | return action_set; | 
|---|
| 297 | } | 
|---|
| 298 |  | 
|---|
| 299 | void OpenXRInterface::free_action_sets() { | 
|---|
| 300 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 301 |  | 
|---|
| 302 | for (int i = 0; i < action_sets.size(); i++) { | 
|---|
| 303 | ActionSet *action_set = action_sets[i]; | 
|---|
| 304 |  | 
|---|
| 305 | free_actions(action_set); | 
|---|
| 306 |  | 
|---|
| 307 | openxr_api->action_set_free(action_set->action_set_rid); | 
|---|
| 308 |  | 
|---|
| 309 | memfree(action_set); | 
|---|
| 310 | } | 
|---|
| 311 | action_sets.clear(); | 
|---|
| 312 | } | 
|---|
| 313 |  | 
|---|
| 314 | OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, const String &p_action_name, const String &p_localized_name, OpenXRAction::ActionType p_action_type, const Vector<Tracker *> p_trackers) { | 
|---|
| 315 | ERR_FAIL_NULL_V(openxr_api, nullptr); | 
|---|
| 316 |  | 
|---|
| 317 | for (int i = 0; i < p_action_set->actions.size(); i++) { | 
|---|
| 318 | if (p_action_set->actions[i]->action_name == p_action_name) { | 
|---|
| 319 | // already exists in this set | 
|---|
| 320 | return nullptr; | 
|---|
| 321 | } | 
|---|
| 322 | } | 
|---|
| 323 |  | 
|---|
| 324 | Vector<RID> tracker_rids; | 
|---|
| 325 | for (int i = 0; i < p_trackers.size(); i++) { | 
|---|
| 326 | tracker_rids.push_back(p_trackers[i]->tracker_rid); | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | Action *action = memnew(Action); | 
|---|
| 330 | if (p_action_type == OpenXRAction::OPENXR_ACTION_POSE) { | 
|---|
| 331 | // We can't have dual action names in OpenXR hence we added _pose, | 
|---|
| 332 | // but default, aim and grip and default pose action names in Godot so rename them on the tracker. | 
|---|
| 333 | // NOTE need to decide on whether we should keep the naming convention or rename it on Godots side | 
|---|
| 334 | if (p_action_name == "default_pose") { | 
|---|
| 335 | action->action_name = "default"; | 
|---|
| 336 | } else if (p_action_name == "aim_pose") { | 
|---|
| 337 | action->action_name = "aim"; | 
|---|
| 338 | } else if (p_action_name == "grip_pose") { | 
|---|
| 339 | action->action_name = "grip"; | 
|---|
| 340 | } else { | 
|---|
| 341 | action->action_name = p_action_name; | 
|---|
| 342 | } | 
|---|
| 343 | } else { | 
|---|
| 344 | action->action_name = p_action_name; | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 | action->action_type = p_action_type; | 
|---|
| 348 | action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids); | 
|---|
| 349 | p_action_set->actions.push_back(action); | 
|---|
| 350 |  | 
|---|
| 351 | // we link our actions back to our trackers so we know which actions to check when we're processing our trackers | 
|---|
| 352 | for (int i = 0; i < p_trackers.size(); i++) { | 
|---|
| 353 | if (p_trackers[i]->actions.find(action) == -1) { | 
|---|
| 354 | p_trackers[i]->actions.push_back(action); | 
|---|
| 355 | } | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | return action; | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | OpenXRInterface::Action *OpenXRInterface::find_action(const String &p_action_name) { | 
|---|
| 362 | // We just find the first action by this name | 
|---|
| 363 |  | 
|---|
| 364 | for (int i = 0; i < action_sets.size(); i++) { | 
|---|
| 365 | for (int j = 0; j < action_sets[i]->actions.size(); j++) { | 
|---|
| 366 | if (action_sets[i]->actions[j]->action_name == p_action_name) { | 
|---|
| 367 | return action_sets[i]->actions[j]; | 
|---|
| 368 | } | 
|---|
| 369 | } | 
|---|
| 370 | } | 
|---|
| 371 |  | 
|---|
| 372 | // not found | 
|---|
| 373 | return nullptr; | 
|---|
| 374 | } | 
|---|
| 375 |  | 
|---|
| 376 | void OpenXRInterface::free_actions(ActionSet *p_action_set) { | 
|---|
| 377 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 378 |  | 
|---|
| 379 | for (int i = 0; i < p_action_set->actions.size(); i++) { | 
|---|
| 380 | Action *action = p_action_set->actions[i]; | 
|---|
| 381 |  | 
|---|
| 382 | openxr_api->action_free(action->action_rid); | 
|---|
| 383 |  | 
|---|
| 384 | memdelete(action); | 
|---|
| 385 | } | 
|---|
| 386 | p_action_set->actions.clear(); | 
|---|
| 387 | } | 
|---|
| 388 |  | 
|---|
| 389 | OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_name, bool p_create) { | 
|---|
| 390 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 391 | ERR_FAIL_NULL_V(xr_server, nullptr); | 
|---|
| 392 | ERR_FAIL_NULL_V(openxr_api, nullptr); | 
|---|
| 393 |  | 
|---|
| 394 | Tracker *tracker = nullptr; | 
|---|
| 395 | for (int i = 0; i < trackers.size(); i++) { | 
|---|
| 396 | tracker = trackers[i]; | 
|---|
| 397 | if (tracker->tracker_name == p_tracker_name) { | 
|---|
| 398 | return tracker; | 
|---|
| 399 | } | 
|---|
| 400 | } | 
|---|
| 401 |  | 
|---|
| 402 | if (!p_create) { | 
|---|
| 403 | return nullptr; | 
|---|
| 404 | } | 
|---|
| 405 |  | 
|---|
| 406 | ERR_FAIL_COND_V(!openxr_api->is_top_level_path_supported(p_tracker_name), nullptr); | 
|---|
| 407 |  | 
|---|
| 408 | // Create our RID | 
|---|
| 409 | RID tracker_rid = openxr_api->tracker_create(p_tracker_name); | 
|---|
| 410 | ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr); | 
|---|
| 411 |  | 
|---|
| 412 | // create our positional tracker | 
|---|
| 413 | Ref<XRPositionalTracker> positional_tracker; | 
|---|
| 414 | positional_tracker.instantiate(); | 
|---|
| 415 |  | 
|---|
| 416 | // We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these. | 
|---|
| 417 | if (p_tracker_name == "/user/hand/left") { | 
|---|
| 418 | positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); | 
|---|
| 419 | positional_tracker->set_tracker_name( "left_hand"); | 
|---|
| 420 | positional_tracker->set_tracker_desc( "Left hand controller"); | 
|---|
| 421 | positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT); | 
|---|
| 422 | } else if (p_tracker_name == "/user/hand/right") { | 
|---|
| 423 | positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); | 
|---|
| 424 | positional_tracker->set_tracker_name( "right_hand"); | 
|---|
| 425 | positional_tracker->set_tracker_desc( "Right hand controller"); | 
|---|
| 426 | positional_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT); | 
|---|
| 427 | } else { | 
|---|
| 428 | positional_tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER); | 
|---|
| 429 | positional_tracker->set_tracker_name(p_tracker_name); | 
|---|
| 430 | positional_tracker->set_tracker_desc(p_tracker_name); | 
|---|
| 431 | } | 
|---|
| 432 | positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE); | 
|---|
| 433 | xr_server->add_tracker(positional_tracker); | 
|---|
| 434 |  | 
|---|
| 435 | // create a new entry | 
|---|
| 436 | tracker = memnew(Tracker); | 
|---|
| 437 | tracker->tracker_name = p_tracker_name; | 
|---|
| 438 | tracker->tracker_rid = tracker_rid; | 
|---|
| 439 | tracker->positional_tracker = positional_tracker; | 
|---|
| 440 | tracker->interaction_profile = RID(); | 
|---|
| 441 | trackers.push_back(tracker); | 
|---|
| 442 |  | 
|---|
| 443 | return tracker; | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 | void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_profile) { | 
|---|
| 447 | Tracker *tracker = nullptr; | 
|---|
| 448 | for (int i = 0; i < trackers.size() && tracker == nullptr; i++) { | 
|---|
| 449 | if (trackers[i]->tracker_rid == p_tracker) { | 
|---|
| 450 | tracker = trackers[i]; | 
|---|
| 451 | } | 
|---|
| 452 | } | 
|---|
| 453 | ERR_FAIL_NULL(tracker); | 
|---|
| 454 |  | 
|---|
| 455 | tracker->interaction_profile = p_interaction_profile; | 
|---|
| 456 |  | 
|---|
| 457 | if (p_interaction_profile.is_null()) { | 
|---|
| 458 | print_verbose( "OpenXR: Interaction profile for "+ tracker->tracker_name + " changed to "+ INTERACTION_PROFILE_NONE); | 
|---|
| 459 | tracker->positional_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE); | 
|---|
| 460 | } else { | 
|---|
| 461 | String name = openxr_api->interaction_profile_get_name(p_interaction_profile); | 
|---|
| 462 | print_verbose( "OpenXR: Interaction profile for "+ tracker->tracker_name + " changed to "+ name); | 
|---|
| 463 | tracker->positional_tracker->set_tracker_profile(name); | 
|---|
| 464 | } | 
|---|
| 465 | } | 
|---|
| 466 |  | 
|---|
| 467 | void OpenXRInterface::handle_tracker(Tracker *p_tracker) { | 
|---|
| 468 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 469 | ERR_FAIL_COND(p_tracker->positional_tracker.is_null()); | 
|---|
| 470 |  | 
|---|
| 471 | // Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction | 
|---|
| 472 | // profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these | 
|---|
| 473 | // and rebind them or even offer bindings to controllers that are not known to us. | 
|---|
| 474 |  | 
|---|
| 475 | // We don't really have a consistent way to detect whether a controller is active however as long as it is | 
|---|
| 476 | // unbound it seems to be unavailable, so far unknown controller seem to mimic one of the profiles we've | 
|---|
| 477 | // supplied. | 
|---|
| 478 | if (p_tracker->interaction_profile.is_null()) { | 
|---|
| 479 | return; | 
|---|
| 480 | } | 
|---|
| 481 |  | 
|---|
| 482 | // We check all actions that are related to our tracker. | 
|---|
| 483 | for (int i = 0; i < p_tracker->actions.size(); i++) { | 
|---|
| 484 | Action *action = p_tracker->actions[i]; | 
|---|
| 485 | switch (action->action_type) { | 
|---|
| 486 | case OpenXRAction::OPENXR_ACTION_BOOL: { | 
|---|
| 487 | bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid); | 
|---|
| 488 | p_tracker->positional_tracker->set_input(action->action_name, Variant(pressed)); | 
|---|
| 489 | } break; | 
|---|
| 490 | case OpenXRAction::OPENXR_ACTION_FLOAT: { | 
|---|
| 491 | real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid); | 
|---|
| 492 | p_tracker->positional_tracker->set_input(action->action_name, Variant(value)); | 
|---|
| 493 | } break; | 
|---|
| 494 | case OpenXRAction::OPENXR_ACTION_VECTOR2: { | 
|---|
| 495 | Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid); | 
|---|
| 496 | p_tracker->positional_tracker->set_input(action->action_name, Variant(value)); | 
|---|
| 497 | } break; | 
|---|
| 498 | case OpenXRAction::OPENXR_ACTION_POSE: { | 
|---|
| 499 | Transform3D transform; | 
|---|
| 500 | Vector3 linear, angular; | 
|---|
| 501 |  | 
|---|
| 502 | XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular); | 
|---|
| 503 |  | 
|---|
| 504 | if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) { | 
|---|
| 505 | p_tracker->positional_tracker->set_pose(action->action_name, transform, linear, angular, confidence); | 
|---|
| 506 | } else { | 
|---|
| 507 | p_tracker->positional_tracker->invalidate_pose(action->action_name); | 
|---|
| 508 | } | 
|---|
| 509 | } break; | 
|---|
| 510 | default: { | 
|---|
| 511 | // not yet supported | 
|---|
| 512 | } break; | 
|---|
| 513 | } | 
|---|
| 514 | } | 
|---|
| 515 | } | 
|---|
| 516 |  | 
|---|
| 517 | void 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) { | 
|---|
| 518 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 519 |  | 
|---|
| 520 | Action *action = find_action(p_action_name); | 
|---|
| 521 | ERR_FAIL_NULL(action); | 
|---|
| 522 |  | 
|---|
| 523 | // We need to map our tracker name to our OpenXR name for our inbuild names. | 
|---|
| 524 | String tracker_name = p_tracker_name; | 
|---|
| 525 | if (tracker_name == "left_hand") { | 
|---|
| 526 | tracker_name = "/user/hand/left"; | 
|---|
| 527 | } else if (tracker_name == "right_hand") { | 
|---|
| 528 | tracker_name = "/user/hand/right"; | 
|---|
| 529 | } | 
|---|
| 530 | Tracker *tracker = find_tracker(tracker_name); | 
|---|
| 531 | ERR_FAIL_NULL(tracker); | 
|---|
| 532 |  | 
|---|
| 533 | // TODO OpenXR does not support delay, so we may need to add support for that somehow... | 
|---|
| 534 |  | 
|---|
| 535 | XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds | 
|---|
| 536 |  | 
|---|
| 537 | openxr_api->trigger_haptic_pulse(action->action_rid, tracker->tracker_rid, p_frequency, p_amplitude, duration); | 
|---|
| 538 | } | 
|---|
| 539 |  | 
|---|
| 540 | void OpenXRInterface::free_trackers() { | 
|---|
| 541 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 542 | ERR_FAIL_NULL(xr_server); | 
|---|
| 543 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 544 |  | 
|---|
| 545 | for (int i = 0; i < trackers.size(); i++) { | 
|---|
| 546 | Tracker *tracker = trackers[i]; | 
|---|
| 547 |  | 
|---|
| 548 | openxr_api->tracker_free(tracker->tracker_rid); | 
|---|
| 549 | xr_server->remove_tracker(tracker->positional_tracker); | 
|---|
| 550 | tracker->positional_tracker.unref(); | 
|---|
| 551 |  | 
|---|
| 552 | memdelete(tracker); | 
|---|
| 553 | } | 
|---|
| 554 | trackers.clear(); | 
|---|
| 555 | } | 
|---|
| 556 |  | 
|---|
| 557 | void OpenXRInterface::free_interaction_profiles() { | 
|---|
| 558 | ERR_FAIL_NULL(openxr_api); | 
|---|
| 559 |  | 
|---|
| 560 | for (int i = 0; i < interaction_profiles.size(); i++) { | 
|---|
| 561 | openxr_api->interaction_profile_free(interaction_profiles[i]); | 
|---|
| 562 | } | 
|---|
| 563 | interaction_profiles.clear(); | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | bool OpenXRInterface::initialize_on_startup() const { | 
|---|
| 567 | if (openxr_api == nullptr) { | 
|---|
| 568 | return false; | 
|---|
| 569 | } else if (!openxr_api->is_initialized()) { | 
|---|
| 570 | return false; | 
|---|
| 571 | } else { | 
|---|
| 572 | return true; | 
|---|
| 573 | } | 
|---|
| 574 | } | 
|---|
| 575 |  | 
|---|
| 576 | bool OpenXRInterface::is_initialized() const { | 
|---|
| 577 | return initialized; | 
|---|
| 578 | }; | 
|---|
| 579 |  | 
|---|
| 580 | bool OpenXRInterface::initialize() { | 
|---|
| 581 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 582 | ERR_FAIL_NULL_V(xr_server, false); | 
|---|
| 583 |  | 
|---|
| 584 | if (openxr_api == nullptr) { | 
|---|
| 585 | return false; | 
|---|
| 586 | } else if (!openxr_api->is_initialized()) { | 
|---|
| 587 | return false; | 
|---|
| 588 | } else if (initialized) { | 
|---|
| 589 | return true; | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | // load up our action sets before setting up our session, note that our profiles are suggestions, OpenXR takes ownership of (re)binding | 
|---|
| 593 | _load_action_map(); | 
|---|
| 594 |  | 
|---|
| 595 | if (!openxr_api->initialize_session()) { | 
|---|
| 596 | return false; | 
|---|
| 597 | } | 
|---|
| 598 |  | 
|---|
| 599 | // we must create a tracker for our head | 
|---|
| 600 | head.instantiate(); | 
|---|
| 601 | head->set_tracker_type(XRServer::TRACKER_HEAD); | 
|---|
| 602 | head->set_tracker_name( "head"); | 
|---|
| 603 | head->set_tracker_desc( "Players head"); | 
|---|
| 604 | xr_server->add_tracker(head); | 
|---|
| 605 |  | 
|---|
| 606 | // attach action sets | 
|---|
| 607 | Vector<RID> loaded_action_sets; | 
|---|
| 608 | for (int i = 0; i < action_sets.size(); i++) { | 
|---|
| 609 | loaded_action_sets.append(action_sets[i]->action_set_rid); | 
|---|
| 610 | } | 
|---|
| 611 | openxr_api->attach_action_sets(loaded_action_sets); | 
|---|
| 612 |  | 
|---|
| 613 | // make this our primary interface | 
|---|
| 614 | xr_server->set_primary_interface(this); | 
|---|
| 615 |  | 
|---|
| 616 | initialized = true; | 
|---|
| 617 |  | 
|---|
| 618 | return initialized; | 
|---|
| 619 | } | 
|---|
| 620 |  | 
|---|
| 621 | void OpenXRInterface::uninitialize() { | 
|---|
| 622 | // Our OpenXR driver will clean itself up properly when Godot exits, so we just do some basic stuff here | 
|---|
| 623 |  | 
|---|
| 624 | // end the session if we need to? | 
|---|
| 625 |  | 
|---|
| 626 | // cleanup stuff | 
|---|
| 627 | free_trackers(); | 
|---|
| 628 | free_interaction_profiles(); | 
|---|
| 629 | free_action_sets(); | 
|---|
| 630 |  | 
|---|
| 631 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 632 | if (xr_server) { | 
|---|
| 633 | if (head.is_valid()) { | 
|---|
| 634 | xr_server->remove_tracker(head); | 
|---|
| 635 | head.unref(); | 
|---|
| 636 | } | 
|---|
| 637 | } | 
|---|
| 638 |  | 
|---|
| 639 | initialized = false; | 
|---|
| 640 | } | 
|---|
| 641 |  | 
|---|
| 642 | Dictionary OpenXRInterface::get_system_info() { | 
|---|
| 643 | Dictionary dict; | 
|---|
| 644 |  | 
|---|
| 645 | if (openxr_api) { | 
|---|
| 646 | dict[SNAME( "XRRuntimeName")] = openxr_api->get_runtime_name(); | 
|---|
| 647 | dict[SNAME( "XRRuntimeVersion")] = openxr_api->get_runtime_version(); | 
|---|
| 648 | } | 
|---|
| 649 |  | 
|---|
| 650 | return dict; | 
|---|
| 651 | } | 
|---|
| 652 |  | 
|---|
| 653 | bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) { | 
|---|
| 654 | return false; | 
|---|
| 655 | } | 
|---|
| 656 |  | 
|---|
| 657 | XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const { | 
|---|
| 658 | return XRInterface::XR_PLAY_AREA_UNKNOWN; | 
|---|
| 659 | } | 
|---|
| 660 |  | 
|---|
| 661 | bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) { | 
|---|
| 662 | return false; | 
|---|
| 663 | } | 
|---|
| 664 |  | 
|---|
| 665 | float OpenXRInterface::get_display_refresh_rate() const { | 
|---|
| 666 | if (openxr_api == nullptr) { | 
|---|
| 667 | return 0.0; | 
|---|
| 668 | } else if (!openxr_api->is_initialized()) { | 
|---|
| 669 | return 0.0; | 
|---|
| 670 | } else { | 
|---|
| 671 | return openxr_api->get_display_refresh_rate(); | 
|---|
| 672 | } | 
|---|
| 673 | } | 
|---|
| 674 |  | 
|---|
| 675 | void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) { | 
|---|
| 676 | if (openxr_api == nullptr) { | 
|---|
| 677 | return; | 
|---|
| 678 | } else if (!openxr_api->is_initialized()) { | 
|---|
| 679 | return; | 
|---|
| 680 | } else { | 
|---|
| 681 | openxr_api->set_display_refresh_rate(p_refresh_rate); | 
|---|
| 682 | } | 
|---|
| 683 | } | 
|---|
| 684 |  | 
|---|
| 685 | Array OpenXRInterface::get_available_display_refresh_rates() const { | 
|---|
| 686 | if (openxr_api == nullptr) { | 
|---|
| 687 | return Array(); | 
|---|
| 688 | } else if (!openxr_api->is_initialized()) { | 
|---|
| 689 | return Array(); | 
|---|
| 690 | } else { | 
|---|
| 691 | return openxr_api->get_available_display_refresh_rates(); | 
|---|
| 692 | } | 
|---|
| 693 | } | 
|---|
| 694 |  | 
|---|
| 695 | bool OpenXRInterface::is_action_set_active(const String &p_action_set) const { | 
|---|
| 696 | for (ActionSet *action_set : action_sets) { | 
|---|
| 697 | if (action_set->action_set_name == p_action_set) { | 
|---|
| 698 | return action_set->is_active; | 
|---|
| 699 | } | 
|---|
| 700 | } | 
|---|
| 701 |  | 
|---|
| 702 | WARN_PRINT( "OpenXR: Unknown action set "+ p_action_set); | 
|---|
| 703 | return false; | 
|---|
| 704 | } | 
|---|
| 705 |  | 
|---|
| 706 | void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) { | 
|---|
| 707 | for (ActionSet *action_set : action_sets) { | 
|---|
| 708 | if (action_set->action_set_name == p_action_set) { | 
|---|
| 709 | action_set->is_active = p_active; | 
|---|
| 710 | return; | 
|---|
| 711 | } | 
|---|
| 712 | } | 
|---|
| 713 |  | 
|---|
| 714 | WARN_PRINT( "OpenXR: Unknown action set "+ p_action_set); | 
|---|
| 715 | } | 
|---|
| 716 |  | 
|---|
| 717 | Array OpenXRInterface::get_action_sets() const { | 
|---|
| 718 | Array arr; | 
|---|
| 719 |  | 
|---|
| 720 | for (ActionSet *action_set : action_sets) { | 
|---|
| 721 | arr.push_back(action_set->action_set_name); | 
|---|
| 722 | } | 
|---|
| 723 |  | 
|---|
| 724 | return arr; | 
|---|
| 725 | } | 
|---|
| 726 |  | 
|---|
| 727 | double OpenXRInterface::get_render_target_size_multiplier() const { | 
|---|
| 728 | if (openxr_api == nullptr) { | 
|---|
| 729 | return 1.0; | 
|---|
| 730 | } else { | 
|---|
| 731 | return openxr_api->get_render_target_size_multiplier(); | 
|---|
| 732 | } | 
|---|
| 733 | } | 
|---|
| 734 |  | 
|---|
| 735 | void OpenXRInterface::set_render_target_size_multiplier(double multiplier) { | 
|---|
| 736 | if (openxr_api == nullptr) { | 
|---|
| 737 | return; | 
|---|
| 738 | } else { | 
|---|
| 739 | openxr_api->set_render_target_size_multiplier(multiplier); | 
|---|
| 740 | } | 
|---|
| 741 | } | 
|---|
| 742 |  | 
|---|
| 743 | Size2 OpenXRInterface::get_render_target_size() { | 
|---|
| 744 | if (openxr_api == nullptr) { | 
|---|
| 745 | return Size2(); | 
|---|
| 746 | } else { | 
|---|
| 747 | return openxr_api->get_recommended_target_size(); | 
|---|
| 748 | } | 
|---|
| 749 | } | 
|---|
| 750 |  | 
|---|
| 751 | uint32_t OpenXRInterface::get_view_count() { | 
|---|
| 752 | // TODO set this based on our configuration | 
|---|
| 753 | return 2; | 
|---|
| 754 | } | 
|---|
| 755 |  | 
|---|
| 756 | void OpenXRInterface::_set_default_pos(Transform3D &p_transform, double p_world_scale, uint64_t p_eye) { | 
|---|
| 757 | p_transform = Transform3D(); | 
|---|
| 758 |  | 
|---|
| 759 | // if we're not tracking, don't put our head on the floor... | 
|---|
| 760 | p_transform.origin.y = 1.5 * p_world_scale; | 
|---|
| 761 |  | 
|---|
| 762 | // overkill but.. | 
|---|
| 763 | if (p_eye == 1) { | 
|---|
| 764 | p_transform.origin.x = 0.03 * p_world_scale; | 
|---|
| 765 | } else if (p_eye == 2) { | 
|---|
| 766 | p_transform.origin.x = -0.03 * p_world_scale; | 
|---|
| 767 | } | 
|---|
| 768 | } | 
|---|
| 769 |  | 
|---|
| 770 | Transform3D OpenXRInterface::get_camera_transform() { | 
|---|
| 771 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 772 | ERR_FAIL_NULL_V(xr_server, Transform3D()); | 
|---|
| 773 |  | 
|---|
| 774 | Transform3D hmd_transform; | 
|---|
| 775 | double world_scale = xr_server->get_world_scale(); | 
|---|
| 776 |  | 
|---|
| 777 | // head_transform should be updated in process | 
|---|
| 778 |  | 
|---|
| 779 | hmd_transform.basis = head_transform.basis; | 
|---|
| 780 | hmd_transform.origin = head_transform.origin * world_scale; | 
|---|
| 781 |  | 
|---|
| 782 | return hmd_transform; | 
|---|
| 783 | } | 
|---|
| 784 |  | 
|---|
| 785 | Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) { | 
|---|
| 786 | XRServer *xr_server = XRServer::get_singleton(); | 
|---|
| 787 | ERR_FAIL_NULL_V(xr_server, Transform3D()); | 
|---|
| 788 | ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds."); | 
|---|
| 789 |  | 
|---|
| 790 | Transform3D t; | 
|---|
| 791 | if (openxr_api && openxr_api->get_view_transform(p_view, t)) { | 
|---|
| 792 | // update our cached value if we have a valid transform | 
|---|
| 793 | transform_for_view[p_view] = t; | 
|---|
| 794 | } else { | 
|---|
| 795 | // reuse cached value | 
|---|
| 796 | t = transform_for_view[p_view]; | 
|---|
| 797 | } | 
|---|
| 798 |  | 
|---|
| 799 | // Apply our world scale | 
|---|
| 800 | double world_scale = xr_server->get_world_scale(); | 
|---|
| 801 | t.origin *= world_scale; | 
|---|
| 802 |  | 
|---|
| 803 | return p_cam_transform * xr_server->get_reference_frame() * t; | 
|---|
| 804 | } | 
|---|
| 805 |  | 
|---|
| 806 | Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) { | 
|---|
| 807 | Projection cm; | 
|---|
| 808 | ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds."); | 
|---|
| 809 |  | 
|---|
| 810 | if (openxr_api) { | 
|---|
| 811 | if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) { | 
|---|
| 812 | return cm; | 
|---|
| 813 | } | 
|---|
| 814 | } | 
|---|
| 815 |  | 
|---|
| 816 | // Failed to get from our OpenXR device? Default to some sort of sensible camera matrix.. | 
|---|
| 817 | cm.set_for_hmd(p_view + 1, 1.0, 6.0, 14.5, 4.0, 1.5, p_z_near, p_z_far); | 
|---|
| 818 |  | 
|---|
| 819 | return cm; | 
|---|
| 820 | } | 
|---|
| 821 |  | 
|---|
| 822 | RID OpenXRInterface::get_color_texture() { | 
|---|
| 823 | if (openxr_api) { | 
|---|
| 824 | return openxr_api->get_color_texture(); | 
|---|
| 825 | } else { | 
|---|
| 826 | return RID(); | 
|---|
| 827 | } | 
|---|
| 828 | } | 
|---|
| 829 |  | 
|---|
| 830 | RID OpenXRInterface::get_depth_texture() { | 
|---|
| 831 | if (openxr_api) { | 
|---|
| 832 | return openxr_api->get_depth_texture(); | 
|---|
| 833 | } else { | 
|---|
| 834 | return RID(); | 
|---|
| 835 | } | 
|---|
| 836 | } | 
|---|
| 837 |  | 
|---|
| 838 | void OpenXRInterface::process() { | 
|---|
| 839 | if (openxr_api) { | 
|---|
| 840 | // do our normal process | 
|---|
| 841 | if (openxr_api->process()) { | 
|---|
| 842 | Transform3D t; | 
|---|
| 843 | Vector3 linear_velocity; | 
|---|
| 844 | Vector3 angular_velocity; | 
|---|
| 845 | XRPose::TrackingConfidence confidence = openxr_api->get_head_center(t, linear_velocity, angular_velocity); | 
|---|
| 846 | if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) { | 
|---|
| 847 | // Only update our transform if we have one to update it with | 
|---|
| 848 | // note that poses are stored without world scale and reference frame applied! | 
|---|
| 849 | head_transform = t; | 
|---|
| 850 | head_linear_velocity = linear_velocity; | 
|---|
| 851 | head_angular_velocity = angular_velocity; | 
|---|
| 852 | } | 
|---|
| 853 | } | 
|---|
| 854 |  | 
|---|
| 855 | // handle our action sets.... | 
|---|
| 856 | Vector<RID> active_sets; | 
|---|
| 857 | for (int i = 0; i < action_sets.size(); i++) { | 
|---|
| 858 | if (action_sets[i]->is_active) { | 
|---|
| 859 | active_sets.push_back(action_sets[i]->action_set_rid); | 
|---|
| 860 | } | 
|---|
| 861 | } | 
|---|
| 862 |  | 
|---|
| 863 | if (openxr_api->sync_action_sets(active_sets)) { | 
|---|
| 864 | for (int i = 0; i < trackers.size(); i++) { | 
|---|
| 865 | handle_tracker(trackers[i]); | 
|---|
| 866 | } | 
|---|
| 867 | } | 
|---|
| 868 | } | 
|---|
| 869 |  | 
|---|
| 870 | if (head.is_valid()) { | 
|---|
| 871 | // TODO figure out how to get our velocities | 
|---|
| 872 |  | 
|---|
| 873 | head->set_pose( "default", head_transform, head_linear_velocity, head_angular_velocity); | 
|---|
| 874 |  | 
|---|
| 875 | // TODO set confidence on pose once we support tracking this.. | 
|---|
| 876 | } | 
|---|
| 877 | } | 
|---|
| 878 |  | 
|---|
| 879 | void OpenXRInterface::pre_render() { | 
|---|
| 880 | if (openxr_api) { | 
|---|
| 881 | openxr_api->pre_render(); | 
|---|
| 882 | } | 
|---|
| 883 | } | 
|---|
| 884 |  | 
|---|
| 885 | bool OpenXRInterface::pre_draw_viewport(RID p_render_target) { | 
|---|
| 886 | if (openxr_api) { | 
|---|
| 887 | return openxr_api->pre_draw_viewport(p_render_target); | 
|---|
| 888 | } else { | 
|---|
| 889 | // don't render | 
|---|
| 890 | return false; | 
|---|
| 891 | } | 
|---|
| 892 | } | 
|---|
| 893 |  | 
|---|
| 894 | Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) { | 
|---|
| 895 | Vector<BlitToScreen> blit_to_screen; | 
|---|
| 896 |  | 
|---|
| 897 | #ifndef ANDROID_ENABLED | 
|---|
| 898 | // If separate HMD we should output one eye to screen | 
|---|
| 899 | if (p_screen_rect != Rect2()) { | 
|---|
| 900 | BlitToScreen blit; | 
|---|
| 901 |  | 
|---|
| 902 | blit.render_target = p_render_target; | 
|---|
| 903 | blit.multi_view.use_layer = true; | 
|---|
| 904 | blit.multi_view.layer = 0; | 
|---|
| 905 | blit.lens_distortion.apply = false; | 
|---|
| 906 |  | 
|---|
| 907 | Size2 render_size = get_render_target_size(); | 
|---|
| 908 | Rect2 dst_rect = p_screen_rect; | 
|---|
| 909 | float new_height = dst_rect.size.x * (render_size.y / render_size.x); | 
|---|
| 910 | if (new_height > dst_rect.size.y) { | 
|---|
| 911 | dst_rect.position.y = (0.5 * dst_rect.size.y) - (0.5 * new_height); | 
|---|
| 912 | dst_rect.size.y = new_height; | 
|---|
| 913 | } else { | 
|---|
| 914 | float new_width = dst_rect.size.y * (render_size.x / render_size.y); | 
|---|
| 915 |  | 
|---|
| 916 | dst_rect.position.x = (0.5 * dst_rect.size.x) - (0.5 * new_width); | 
|---|
| 917 | dst_rect.size.x = new_width; | 
|---|
| 918 | } | 
|---|
| 919 |  | 
|---|
| 920 | blit.dst_rect = dst_rect; | 
|---|
| 921 | blit_to_screen.push_back(blit); | 
|---|
| 922 | } | 
|---|
| 923 | #endif | 
|---|
| 924 |  | 
|---|
| 925 | if (openxr_api) { | 
|---|
| 926 | openxr_api->post_draw_viewport(p_render_target); | 
|---|
| 927 | } | 
|---|
| 928 |  | 
|---|
| 929 | return blit_to_screen; | 
|---|
| 930 | } | 
|---|
| 931 |  | 
|---|
| 932 | void OpenXRInterface::end_frame() { | 
|---|
| 933 | if (openxr_api) { | 
|---|
| 934 | openxr_api->end_frame(); | 
|---|
| 935 | } | 
|---|
| 936 | } | 
|---|
| 937 |  | 
|---|
| 938 | bool OpenXRInterface::is_passthrough_supported() { | 
|---|
| 939 | return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_supported(); | 
|---|
| 940 | } | 
|---|
| 941 |  | 
|---|
| 942 | bool OpenXRInterface::is_passthrough_enabled() { | 
|---|
| 943 | return passthrough_wrapper != nullptr && passthrough_wrapper->is_passthrough_enabled(); | 
|---|
| 944 | } | 
|---|
| 945 |  | 
|---|
| 946 | bool OpenXRInterface::start_passthrough() { | 
|---|
| 947 | return passthrough_wrapper != nullptr && passthrough_wrapper->start_passthrough(); | 
|---|
| 948 | } | 
|---|
| 949 |  | 
|---|
| 950 | void OpenXRInterface::stop_passthrough() { | 
|---|
| 951 | if (passthrough_wrapper) { | 
|---|
| 952 | passthrough_wrapper->stop_passthrough(); | 
|---|
| 953 | } | 
|---|
| 954 | } | 
|---|
| 955 |  | 
|---|
| 956 | Array OpenXRInterface::get_supported_environment_blend_modes() { | 
|---|
| 957 | Array modes; | 
|---|
| 958 |  | 
|---|
| 959 | if (!openxr_api) { | 
|---|
| 960 | return modes; | 
|---|
| 961 | } | 
|---|
| 962 |  | 
|---|
| 963 | uint32_t count = 0; | 
|---|
| 964 | const XrEnvironmentBlendMode *env_blend_modes = openxr_api->get_supported_environment_blend_modes(count); | 
|---|
| 965 |  | 
|---|
| 966 | if (!env_blend_modes) { | 
|---|
| 967 | return modes; | 
|---|
| 968 | } | 
|---|
| 969 |  | 
|---|
| 970 | for (uint32_t i = 0; i < count; i++) { | 
|---|
| 971 | switch (env_blend_modes[i]) { | 
|---|
| 972 | case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: | 
|---|
| 973 | modes.push_back(XR_ENV_BLEND_MODE_OPAQUE); | 
|---|
| 974 | break; | 
|---|
| 975 | case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: | 
|---|
| 976 | modes.push_back(XR_ENV_BLEND_MODE_ADDITIVE); | 
|---|
| 977 | break; | 
|---|
| 978 | case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: | 
|---|
| 979 | modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND); | 
|---|
| 980 | break; | 
|---|
| 981 | default: | 
|---|
| 982 | WARN_PRINT( "Unsupported blend mode found: "+ String::num_int64(int64_t(env_blend_modes[i]))); | 
|---|
| 983 | } | 
|---|
| 984 | } | 
|---|
| 985 | return modes; | 
|---|
| 986 | } | 
|---|
| 987 |  | 
|---|
| 988 | XRInterface::EnvironmentBlendMode OpenXRInterface::get_environment_blend_mode() const { | 
|---|
| 989 | if (openxr_api) { | 
|---|
| 990 | XrEnvironmentBlendMode oxr_blend_mode = openxr_api->get_environment_blend_mode(); | 
|---|
| 991 | switch (oxr_blend_mode) { | 
|---|
| 992 | case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: { | 
|---|
| 993 | return XR_ENV_BLEND_MODE_OPAQUE; | 
|---|
| 994 | } break; | 
|---|
| 995 | case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: { | 
|---|
| 996 | return XR_ENV_BLEND_MODE_ADDITIVE; | 
|---|
| 997 | } break; | 
|---|
| 998 | case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: { | 
|---|
| 999 | return XR_ENV_BLEND_MODE_ALPHA_BLEND; | 
|---|
| 1000 | } break; | 
|---|
| 1001 | default: | 
|---|
| 1002 | break; | 
|---|
| 1003 | } | 
|---|
| 1004 | } | 
|---|
| 1005 |  | 
|---|
| 1006 | return XR_ENV_BLEND_MODE_OPAQUE; | 
|---|
| 1007 | } | 
|---|
| 1008 |  | 
|---|
| 1009 | bool OpenXRInterface::set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) { | 
|---|
| 1010 | if (openxr_api) { | 
|---|
| 1011 | XrEnvironmentBlendMode oxr_blend_mode; | 
|---|
| 1012 | switch (mode) { | 
|---|
| 1013 | case XR_ENV_BLEND_MODE_OPAQUE: | 
|---|
| 1014 | oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; | 
|---|
| 1015 | break; | 
|---|
| 1016 | case XR_ENV_BLEND_MODE_ADDITIVE: | 
|---|
| 1017 | oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE; | 
|---|
| 1018 | break; | 
|---|
| 1019 | case XR_ENV_BLEND_MODE_ALPHA_BLEND: | 
|---|
| 1020 | oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND; | 
|---|
| 1021 | break; | 
|---|
| 1022 | default: | 
|---|
| 1023 | WARN_PRINT( "Unknown blend mode requested: "+ String::num_int64(int64_t(mode))); | 
|---|
| 1024 | oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; | 
|---|
| 1025 | } | 
|---|
| 1026 | return openxr_api->set_environment_blend_mode(oxr_blend_mode); | 
|---|
| 1027 | } | 
|---|
| 1028 | return false; | 
|---|
| 1029 | } | 
|---|
| 1030 |  | 
|---|
| 1031 | void OpenXRInterface::on_state_ready() { | 
|---|
| 1032 | emit_signal(SNAME( "session_begun")); | 
|---|
| 1033 | } | 
|---|
| 1034 |  | 
|---|
| 1035 | void OpenXRInterface::on_state_visible() { | 
|---|
| 1036 | emit_signal(SNAME( "session_visible")); | 
|---|
| 1037 | } | 
|---|
| 1038 |  | 
|---|
| 1039 | void OpenXRInterface::on_state_focused() { | 
|---|
| 1040 | emit_signal(SNAME( "session_focussed")); | 
|---|
| 1041 | } | 
|---|
| 1042 |  | 
|---|
| 1043 | void OpenXRInterface::on_state_stopping() { | 
|---|
| 1044 | emit_signal(SNAME( "session_stopping")); | 
|---|
| 1045 | } | 
|---|
| 1046 |  | 
|---|
| 1047 | void OpenXRInterface::on_pose_recentered() { | 
|---|
| 1048 | emit_signal(SNAME( "pose_recentered")); | 
|---|
| 1049 | } | 
|---|
| 1050 |  | 
|---|
| 1051 | /** Hand tracking. */ | 
|---|
| 1052 | void OpenXRInterface::set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range) { | 
|---|
| 1053 | ERR_FAIL_INDEX(p_hand, HAND_MAX); | 
|---|
| 1054 | ERR_FAIL_INDEX(p_motion_range, HAND_MOTION_RANGE_MAX); | 
|---|
| 1055 |  | 
|---|
| 1056 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1057 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1058 | XrHandJointsMotionRangeEXT xr_motion_range; | 
|---|
| 1059 | switch (p_motion_range) { | 
|---|
| 1060 | case HAND_MOTION_RANGE_UNOBSTRUCTED: | 
|---|
| 1061 | xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT; | 
|---|
| 1062 | break; | 
|---|
| 1063 | case HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER: | 
|---|
| 1064 | xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT; | 
|---|
| 1065 | break; | 
|---|
| 1066 | default: | 
|---|
| 1067 | // Shouldn't get here, ERR_FAIL_INDEX should have caught this... | 
|---|
| 1068 | xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT; | 
|---|
| 1069 | break; | 
|---|
| 1070 | } | 
|---|
| 1071 |  | 
|---|
| 1072 | hand_tracking_ext->set_motion_range(uint32_t(p_hand), xr_motion_range); | 
|---|
| 1073 | } | 
|---|
| 1074 | } | 
|---|
| 1075 |  | 
|---|
| 1076 | OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_hand) const { | 
|---|
| 1077 | ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_MOTION_RANGE_MAX); | 
|---|
| 1078 |  | 
|---|
| 1079 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1080 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1081 | XrHandJointsMotionRangeEXT xr_motion_range = hand_tracking_ext->get_motion_range(uint32_t(p_hand)); | 
|---|
| 1082 |  | 
|---|
| 1083 | switch (xr_motion_range) { | 
|---|
| 1084 | case XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT: | 
|---|
| 1085 | return HAND_MOTION_RANGE_UNOBSTRUCTED; | 
|---|
| 1086 | case XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT: | 
|---|
| 1087 | return HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER; | 
|---|
| 1088 | default: | 
|---|
| 1089 | ERR_FAIL_V_MSG(HAND_MOTION_RANGE_MAX, "Unknown motion range returned by OpenXR"); | 
|---|
| 1090 | } | 
|---|
| 1091 | } | 
|---|
| 1092 |  | 
|---|
| 1093 | return HAND_MOTION_RANGE_MAX; | 
|---|
| 1094 | } | 
|---|
| 1095 |  | 
|---|
| 1096 | Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const { | 
|---|
| 1097 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1098 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1099 | return hand_tracking_ext->get_hand_joint_rotation(uint32_t(p_hand), XrHandJointEXT(p_joint)); | 
|---|
| 1100 | } | 
|---|
| 1101 |  | 
|---|
| 1102 | return Quaternion(); | 
|---|
| 1103 | } | 
|---|
| 1104 |  | 
|---|
| 1105 | Vector3 OpenXRInterface::get_hand_joint_position(Hand p_hand, HandJoints p_joint) const { | 
|---|
| 1106 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1107 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1108 | return hand_tracking_ext->get_hand_joint_position(uint32_t(p_hand), XrHandJointEXT(p_joint)); | 
|---|
| 1109 | } | 
|---|
| 1110 |  | 
|---|
| 1111 | return Vector3(); | 
|---|
| 1112 | } | 
|---|
| 1113 |  | 
|---|
| 1114 | float OpenXRInterface::get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const { | 
|---|
| 1115 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1116 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1117 | return hand_tracking_ext->get_hand_joint_radius(uint32_t(p_hand), XrHandJointEXT(p_joint)); | 
|---|
| 1118 | } | 
|---|
| 1119 |  | 
|---|
| 1120 | return 0.0; | 
|---|
| 1121 | } | 
|---|
| 1122 |  | 
|---|
| 1123 | Vector3 OpenXRInterface::get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const { | 
|---|
| 1124 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1125 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1126 | return hand_tracking_ext->get_hand_joint_linear_velocity(uint32_t(p_hand), XrHandJointEXT(p_joint)); | 
|---|
| 1127 | } | 
|---|
| 1128 |  | 
|---|
| 1129 | return Vector3(); | 
|---|
| 1130 | } | 
|---|
| 1131 |  | 
|---|
| 1132 | Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const { | 
|---|
| 1133 | OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); | 
|---|
| 1134 | if (hand_tracking_ext && hand_tracking_ext->get_active()) { | 
|---|
| 1135 | return hand_tracking_ext->get_hand_joint_angular_velocity(uint32_t(p_hand), XrHandJointEXT(p_joint)); | 
|---|
| 1136 | } | 
|---|
| 1137 |  | 
|---|
| 1138 | return Vector3(); | 
|---|
| 1139 | } | 
|---|
| 1140 |  | 
|---|
| 1141 | OpenXRInterface::OpenXRInterface() { | 
|---|
| 1142 | openxr_api = OpenXRAPI::get_singleton(); | 
|---|
| 1143 | if (openxr_api) { | 
|---|
| 1144 | openxr_api->set_xr_interface(this); | 
|---|
| 1145 | } | 
|---|
| 1146 |  | 
|---|
| 1147 | // while we don't have head tracking, don't put the headset on the floor... | 
|---|
| 1148 | _set_default_pos(head_transform, 1.0, 0); | 
|---|
| 1149 | _set_default_pos(transform_for_view[0], 1.0, 1); | 
|---|
| 1150 | _set_default_pos(transform_for_view[1], 1.0, 2); | 
|---|
| 1151 |  | 
|---|
| 1152 | passthrough_wrapper = OpenXRFbPassthroughExtensionWrapper::get_singleton(); | 
|---|
| 1153 | } | 
|---|
| 1154 |  | 
|---|
| 1155 | OpenXRInterface::~OpenXRInterface() { | 
|---|
| 1156 | if (is_initialized()) { | 
|---|
| 1157 | uninitialize(); | 
|---|
| 1158 | } | 
|---|
| 1159 |  | 
|---|
| 1160 | if (openxr_api) { | 
|---|
| 1161 | openxr_api->set_xr_interface(nullptr); | 
|---|
| 1162 | openxr_api = nullptr; | 
|---|
| 1163 | } | 
|---|
| 1164 | } | 
|---|
| 1165 |  | 
|---|