Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/openxr_interface.cpp
20892 views
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
36
#include "extensions/openxr_eye_gaze_interaction.h"
37
#include "extensions/openxr_hand_interaction_extension.h"
38
#include "extensions/openxr_performance_settings_extension.h"
39
#include "extensions/openxr_user_presence_extension.h"
40
#include "servers/rendering/renderer_compositor.h"
41
42
#include <openxr/openxr.h>
43
44
void OpenXRInterface::_bind_methods() {
45
// lifecycle signals
46
ADD_SIGNAL(MethodInfo("session_begun"));
47
ADD_SIGNAL(MethodInfo("session_stopping"));
48
ADD_SIGNAL(MethodInfo("session_synchronized"));
49
ADD_SIGNAL(MethodInfo("session_focussed"));
50
ADD_SIGNAL(MethodInfo("session_visible"));
51
ADD_SIGNAL(MethodInfo("session_loss_pending"));
52
ADD_SIGNAL(MethodInfo("instance_exiting"));
53
ADD_SIGNAL(MethodInfo("pose_recentered"));
54
ADD_SIGNAL(MethodInfo("refresh_rate_changed", PropertyInfo(Variant::FLOAT, "refresh_rate")));
55
56
ADD_SIGNAL(MethodInfo("cpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));
57
ADD_SIGNAL(MethodInfo("gpu_level_changed", PropertyInfo(Variant::INT, "sub_domain"), PropertyInfo(Variant::INT, "from_level"), PropertyInfo(Variant::INT, "to_level")));
58
59
// State
60
ClassDB::bind_method(D_METHOD("get_session_state"), &OpenXRInterface::get_session_state);
61
62
// User presence
63
ADD_SIGNAL(MethodInfo("user_presence_changed", PropertyInfo(Variant::BOOL, "is_user_present")));
64
65
ClassDB::bind_method(D_METHOD("is_user_presence_supported"), &OpenXRInterface::is_user_presence_supported);
66
ClassDB::bind_method(D_METHOD("is_user_present"), &OpenXRInterface::is_user_present);
67
68
// Display refresh rate
69
ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate);
70
ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate);
71
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate");
72
73
// Render Target size multiplier
74
ClassDB::bind_method(D_METHOD("get_render_target_size_multiplier"), &OpenXRInterface::get_render_target_size_multiplier);
75
ClassDB::bind_method(D_METHOD("set_render_target_size_multiplier", "multiplier"), &OpenXRInterface::set_render_target_size_multiplier);
76
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "render_target_size_multiplier"), "set_render_target_size_multiplier", "get_render_target_size_multiplier");
77
78
// Foveation level
79
ClassDB::bind_method(D_METHOD("is_foveation_supported"), &OpenXRInterface::is_foveation_supported);
80
81
ClassDB::bind_method(D_METHOD("get_foveation_level"), &OpenXRInterface::get_foveation_level);
82
ClassDB::bind_method(D_METHOD("set_foveation_level", "foveation_level"), &OpenXRInterface::set_foveation_level);
83
ADD_PROPERTY(PropertyInfo(Variant::INT, "foveation_level"), "set_foveation_level", "get_foveation_level");
84
85
ClassDB::bind_method(D_METHOD("get_foveation_dynamic"), &OpenXRInterface::get_foveation_dynamic);
86
ClassDB::bind_method(D_METHOD("set_foveation_dynamic", "foveation_dynamic"), &OpenXRInterface::set_foveation_dynamic);
87
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "foveation_dynamic"), "set_foveation_dynamic", "get_foveation_dynamic");
88
89
// Action sets
90
ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active);
91
ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active);
92
ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets);
93
94
// Refresh rates
95
ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates);
96
97
// Hand tracking.
98
ClassDB::bind_method(D_METHOD("set_motion_range", "hand", "motion_range"), &OpenXRInterface::set_motion_range);
99
ClassDB::bind_method(D_METHOD("get_motion_range", "hand"), &OpenXRInterface::get_motion_range);
100
101
ClassDB::bind_method(D_METHOD("get_hand_tracking_source", "hand"), &OpenXRInterface::get_hand_tracking_source);
102
103
ClassDB::bind_method(D_METHOD("get_hand_joint_flags", "hand", "joint"), &OpenXRInterface::get_hand_joint_flags);
104
105
ClassDB::bind_method(D_METHOD("get_hand_joint_rotation", "hand", "joint"), &OpenXRInterface::get_hand_joint_rotation);
106
ClassDB::bind_method(D_METHOD("get_hand_joint_position", "hand", "joint"), &OpenXRInterface::get_hand_joint_position);
107
ClassDB::bind_method(D_METHOD("get_hand_joint_radius", "hand", "joint"), &OpenXRInterface::get_hand_joint_radius);
108
109
ClassDB::bind_method(D_METHOD("get_hand_joint_linear_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_linear_velocity);
110
ClassDB::bind_method(D_METHOD("get_hand_joint_angular_velocity", "hand", "joint"), &OpenXRInterface::get_hand_joint_angular_velocity);
111
112
ClassDB::bind_method(D_METHOD("is_hand_tracking_supported"), &OpenXRInterface::is_hand_tracking_supported);
113
ClassDB::bind_method(D_METHOD("is_hand_interaction_supported"), &OpenXRInterface::is_hand_interaction_supported);
114
ClassDB::bind_method(D_METHOD("is_eye_gaze_interaction_supported"), &OpenXRInterface::is_eye_gaze_interaction_supported);
115
116
// VRS
117
ClassDB::bind_method(D_METHOD("get_vrs_min_radius"), &OpenXRInterface::get_vrs_min_radius);
118
ClassDB::bind_method(D_METHOD("set_vrs_min_radius", "radius"), &OpenXRInterface::set_vrs_min_radius);
119
ClassDB::bind_method(D_METHOD("get_vrs_strength"), &OpenXRInterface::get_vrs_strength);
120
ClassDB::bind_method(D_METHOD("set_vrs_strength", "strength"), &OpenXRInterface::set_vrs_strength);
121
122
// Performance settings.
123
ClassDB::bind_method(D_METHOD("set_cpu_level", "level"), &OpenXRInterface::set_cpu_level);
124
ClassDB::bind_method(D_METHOD("set_gpu_level", "level"), &OpenXRInterface::set_gpu_level);
125
126
ADD_GROUP("Vulkan VRS", "vrs_");
127
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_min_radius", PROPERTY_HINT_RANGE, "1.0,100.0,1.0"), "set_vrs_min_radius", "get_vrs_min_radius");
128
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vrs_strength", PROPERTY_HINT_RANGE, "0.1,10.0,0.1"), "set_vrs_strength", "get_vrs_strength");
129
130
BIND_ENUM_CONSTANT(SESSION_STATE_UNKNOWN);
131
BIND_ENUM_CONSTANT(SESSION_STATE_IDLE);
132
BIND_ENUM_CONSTANT(SESSION_STATE_READY);
133
BIND_ENUM_CONSTANT(SESSION_STATE_SYNCHRONIZED);
134
BIND_ENUM_CONSTANT(SESSION_STATE_VISIBLE);
135
BIND_ENUM_CONSTANT(SESSION_STATE_FOCUSED);
136
BIND_ENUM_CONSTANT(SESSION_STATE_STOPPING);
137
BIND_ENUM_CONSTANT(SESSION_STATE_LOSS_PENDING);
138
BIND_ENUM_CONSTANT(SESSION_STATE_EXITING);
139
140
BIND_ENUM_CONSTANT(HAND_LEFT);
141
BIND_ENUM_CONSTANT(HAND_RIGHT);
142
BIND_ENUM_CONSTANT(HAND_MAX);
143
144
BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_UNOBSTRUCTED);
145
BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER);
146
BIND_ENUM_CONSTANT(HAND_MOTION_RANGE_MAX);
147
148
BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNKNOWN);
149
BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_UNOBSTRUCTED);
150
BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_CONTROLLER);
151
BIND_ENUM_CONSTANT(HAND_TRACKED_SOURCE_MAX);
152
153
BIND_ENUM_CONSTANT(HAND_JOINT_PALM);
154
BIND_ENUM_CONSTANT(HAND_JOINT_WRIST);
155
BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_METACARPAL);
156
BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_PROXIMAL);
157
BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_DISTAL);
158
BIND_ENUM_CONSTANT(HAND_JOINT_THUMB_TIP);
159
BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_METACARPAL);
160
BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_PROXIMAL);
161
BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_INTERMEDIATE);
162
BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_DISTAL);
163
BIND_ENUM_CONSTANT(HAND_JOINT_INDEX_TIP);
164
BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_METACARPAL);
165
BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_PROXIMAL);
166
BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_INTERMEDIATE);
167
BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_DISTAL);
168
BIND_ENUM_CONSTANT(HAND_JOINT_MIDDLE_TIP);
169
BIND_ENUM_CONSTANT(HAND_JOINT_RING_METACARPAL);
170
BIND_ENUM_CONSTANT(HAND_JOINT_RING_PROXIMAL);
171
BIND_ENUM_CONSTANT(HAND_JOINT_RING_INTERMEDIATE);
172
BIND_ENUM_CONSTANT(HAND_JOINT_RING_DISTAL);
173
BIND_ENUM_CONSTANT(HAND_JOINT_RING_TIP);
174
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_METACARPAL);
175
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_PROXIMAL);
176
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_INTERMEDIATE);
177
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_DISTAL);
178
BIND_ENUM_CONSTANT(HAND_JOINT_LITTLE_TIP);
179
BIND_ENUM_CONSTANT(HAND_JOINT_MAX);
180
181
BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_POWER_SAVINGS);
182
BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_LOW);
183
BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_SUSTAINED_HIGH);
184
BIND_ENUM_CONSTANT(PERF_SETTINGS_LEVEL_BOOST);
185
186
BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_COMPOSITING);
187
BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_RENDERING);
188
BIND_ENUM_CONSTANT(PERF_SETTINGS_SUB_DOMAIN_THERMAL);
189
190
BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_NORMAL);
191
BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_WARNING);
192
BIND_ENUM_CONSTANT(PERF_SETTINGS_NOTIF_LEVEL_IMPAIRED);
193
194
BIND_BITFIELD_FLAG(HAND_JOINT_NONE);
195
BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_VALID);
196
BIND_BITFIELD_FLAG(HAND_JOINT_ORIENTATION_TRACKED);
197
BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_VALID);
198
BIND_BITFIELD_FLAG(HAND_JOINT_POSITION_TRACKED);
199
BIND_BITFIELD_FLAG(HAND_JOINT_LINEAR_VELOCITY_VALID);
200
BIND_BITFIELD_FLAG(HAND_JOINT_ANGULAR_VELOCITY_VALID);
201
}
202
203
StringName OpenXRInterface::get_name() const {
204
return StringName("OpenXR");
205
}
206
207
uint32_t OpenXRInterface::get_capabilities() const {
208
return XRInterface::XR_VR + XRInterface::XR_STEREO;
209
}
210
211
PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {
212
// These are hardcoded in OpenXR, note that they will only be available if added to our action map
213
214
PackedStringArray arr = {
215
"head", // XRPositionalTracker for the users head (Mapped from OpenXR /user/head)
216
"left_hand", // XRControllerTracker for the users left hand (Mapped from OpenXR /user/hand/left)
217
"right_hand", // XRControllerTracker for the users right hand (Mapped from OpenXR /user/hand/right)
218
"/user/hand_tracker/left", // XRHandTracker for the users left hand
219
"/user/hand_tracker/right", // XRHandTracker for the users right hand
220
"/user/body_tracker", // XRBodyTracker for the users body
221
"/user/face_tracker", // XRFaceTracker for the users face
222
"/user/treadmill"
223
};
224
225
for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {
226
arr.append_array(wrapper->get_suggested_tracker_names());
227
}
228
229
return arr;
230
}
231
232
XRInterface::TrackingStatus OpenXRInterface::get_tracking_status() const {
233
return tracking_state;
234
}
235
236
void OpenXRInterface::_load_action_map() {
237
ERR_FAIL_NULL(openxr_api);
238
239
// This may seem a bit duplicitous to a little bit of background info here.
240
// OpenXRActionMap (with all its sub resource classes) is a class that allows us to configure and store an action map in.
241
// This gives the user the ability to edit the action map in a UI and customize the actions.
242
// OpenXR however requires us to submit an action map and it takes over from that point and we can no longer change it.
243
// This system does that push and we store the info needed to then work with this action map going forward.
244
245
// Within our openxr device we maintain a number of classes that wrap the relevant OpenXR objects for this.
246
// Within OpenXRInterface we have a few internal classes that keep track of what we've created.
247
// This allow us to process the relevant actions each frame.
248
249
// just in case clean up
250
free_trackers();
251
free_interaction_profiles();
252
free_action_sets();
253
254
Ref<OpenXRActionMap> action_map;
255
if (Engine::get_singleton()->is_editor_hint()) {
256
#ifdef TOOLS_ENABLED
257
action_map.instantiate();
258
action_map->create_editor_action_sets();
259
#endif
260
} else {
261
String default_tres_name = openxr_api->get_default_action_map_resource_name();
262
263
// Check if we can load our default
264
if (ResourceLoader::exists(default_tres_name)) {
265
action_map = ResourceLoader::load(default_tres_name);
266
}
267
268
// Check if we need to create default action set
269
if (action_map.is_null()) {
270
action_map.instantiate();
271
action_map->create_default_action_sets();
272
#ifdef TOOLS_ENABLED
273
// Save our action sets so our user can
274
action_map->set_path(default_tres_name, true);
275
ResourceSaver::save(action_map, default_tres_name);
276
#endif
277
}
278
}
279
280
// process our action map
281
if (action_map.is_valid()) {
282
HashMap<Ref<OpenXRAction>, Action *> xr_actions;
283
284
Array action_set_array = action_map->get_action_sets();
285
for (int i = 0; i < action_set_array.size(); i++) {
286
// Create our action set
287
Ref<OpenXRActionSet> xr_action_set = action_set_array[i];
288
ActionSet *action_set = create_action_set(xr_action_set->get_name(), xr_action_set->get_localized_name(), xr_action_set->get_priority());
289
if (!action_set) {
290
continue;
291
}
292
293
// Now create our actions for these
294
Array actions = xr_action_set->get_actions();
295
for (int j = 0; j < actions.size(); j++) {
296
Ref<OpenXRAction> xr_action = actions[j];
297
298
PackedStringArray toplevel_paths = xr_action->get_toplevel_paths();
299
Vector<Tracker *> trackers_for_action;
300
301
for (int k = 0; k < toplevel_paths.size(); k++) {
302
// Only check for our tracker if our path is supported.
303
if (openxr_api->is_top_level_path_supported(toplevel_paths[k])) {
304
Tracker *tracker = find_tracker(toplevel_paths[k], true);
305
if (tracker) {
306
trackers_for_action.push_back(tracker);
307
}
308
}
309
}
310
311
// Only add our action if we have at least one valid toplevel path
312
if (trackers_for_action.size() > 0) {
313
Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action);
314
if (action) {
315
// add this to our map for creating our interaction profiles
316
xr_actions[xr_action] = action;
317
}
318
}
319
}
320
}
321
322
// now do our suggestions
323
Array interaction_profile_array = action_map->get_interaction_profiles();
324
for (int i = 0; i < interaction_profile_array.size(); i++) {
325
Ref<OpenXRInteractionProfile> xr_interaction_profile = interaction_profile_array[i];
326
327
// Note, we can only have one entry per interaction profile so if it already exists we clear it out
328
RID ip = openxr_api->interaction_profile_create(xr_interaction_profile->get_interaction_profile_path());
329
if (ip.is_valid()) {
330
openxr_api->interaction_profile_clear_bindings(ip);
331
332
for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_interaction_profile->get_binding_modifiers()) {
333
PackedByteArray bm = xr_binding_modifier->get_ip_modification();
334
if (!bm.is_empty()) {
335
openxr_api->interaction_profile_add_modifier(ip, bm);
336
}
337
}
338
339
Array xr_bindings = xr_interaction_profile->get_bindings();
340
for (int j = 0; j < xr_bindings.size(); j++) {
341
Ref<OpenXRIPBinding> xr_binding = xr_bindings[j];
342
Ref<OpenXRAction> xr_action = xr_binding->get_action();
343
344
Action *action = nullptr;
345
if (xr_actions.has(xr_action)) {
346
action = xr_actions[xr_action];
347
} else {
348
print_line("Action ", xr_action->get_name(), " isn't part of an action set!");
349
continue;
350
}
351
352
int binding_no = openxr_api->interaction_profile_add_binding(ip, action->action_rid, xr_binding->get_binding_path());
353
if (binding_no >= 0) {
354
for (Ref<OpenXRBindingModifier> xr_binding_modifier : xr_binding->get_binding_modifiers()) {
355
// Binding modifiers on bindings can be added to the interaction profile.
356
PackedByteArray bm = xr_binding_modifier->get_ip_modification();
357
if (!bm.is_empty()) {
358
openxr_api->interaction_profile_add_modifier(ip, bm);
359
}
360
361
// And possibly in the future on the binding itself, we're just preparing for that eventuality.
362
}
363
}
364
}
365
366
// Now submit our suggestions
367
openxr_api->interaction_profile_suggest_bindings(ip);
368
369
// And record it in our array so we can clean it up later on
370
if (interaction_profile_array.has(ip)) {
371
interaction_profile_array.push_back(ip);
372
}
373
}
374
}
375
}
376
}
377
378
OpenXRInterface::ActionSet *OpenXRInterface::create_action_set(const String &p_action_set_name, const String &p_localized_name, const int p_priority) {
379
ERR_FAIL_NULL_V(openxr_api, nullptr);
380
381
// find if it already exists
382
for (int i = 0; i < action_sets.size(); i++) {
383
if (action_sets[i]->action_set_name == p_action_set_name) {
384
// already exists in this set
385
return nullptr;
386
}
387
}
388
389
ActionSet *action_set = memnew(ActionSet);
390
action_set->action_set_name = p_action_set_name;
391
action_set->is_active = true;
392
action_set->action_set_rid = openxr_api->action_set_create(p_action_set_name, p_localized_name, p_priority);
393
action_sets.push_back(action_set);
394
395
return action_set;
396
}
397
398
void OpenXRInterface::free_action_sets() {
399
ERR_FAIL_NULL(openxr_api);
400
401
for (int i = 0; i < action_sets.size(); i++) {
402
ActionSet *action_set = action_sets[i];
403
404
free_actions(action_set);
405
406
openxr_api->action_set_free(action_set->action_set_rid);
407
408
memfree(action_set);
409
}
410
action_sets.clear();
411
}
412
413
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) {
414
ERR_FAIL_NULL_V(openxr_api, nullptr);
415
416
for (int i = 0; i < p_action_set->actions.size(); i++) {
417
if (p_action_set->actions[i]->action_name == p_action_name) {
418
// already exists in this set
419
return nullptr;
420
}
421
}
422
423
Vector<RID> tracker_rids;
424
for (int i = 0; i < p_trackers.size(); i++) {
425
tracker_rids.push_back(p_trackers[i]->tracker_rid);
426
}
427
428
Action *action = memnew(Action);
429
if (p_action_type == OpenXRAction::OPENXR_ACTION_POSE) {
430
// We can't have dual action names in OpenXR hence we added _pose,
431
// but default, aim and grip and default pose action names in Godot so rename them on the tracker.
432
// NOTE need to decide on whether we should keep the naming convention or rename it on Godots side
433
if (p_action_name == "default_pose") {
434
action->action_name = "default";
435
} else if (p_action_name == "aim_pose") {
436
action->action_name = "aim";
437
} else if (p_action_name == "grip_pose") {
438
action->action_name = "grip";
439
} else {
440
action->action_name = p_action_name;
441
}
442
} else {
443
action->action_name = p_action_name;
444
}
445
446
action->action_type = p_action_type;
447
action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids);
448
p_action_set->actions.push_back(action);
449
450
// we link our actions back to our trackers so we know which actions to check when we're processing our trackers
451
for (int i = 0; i < p_trackers.size(); i++) {
452
if (!p_trackers[i]->actions.has(action)) {
453
p_trackers[i]->actions.push_back(action);
454
}
455
}
456
457
return action;
458
}
459
460
OpenXRInterface::Action *OpenXRInterface::find_action(const String &p_action_name) {
461
// We just find the first action by this name
462
463
for (int i = 0; i < action_sets.size(); i++) {
464
for (int j = 0; j < action_sets[i]->actions.size(); j++) {
465
if (action_sets[i]->actions[j]->action_name == p_action_name) {
466
return action_sets[i]->actions[j];
467
}
468
}
469
}
470
471
// not found
472
return nullptr;
473
}
474
475
void OpenXRInterface::free_actions(ActionSet *p_action_set) {
476
ERR_FAIL_NULL(openxr_api);
477
478
for (int i = 0; i < p_action_set->actions.size(); i++) {
479
Action *action = p_action_set->actions[i];
480
481
openxr_api->action_free(action->action_rid);
482
483
memdelete(action);
484
}
485
p_action_set->actions.clear();
486
}
487
488
OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_name, bool p_create) {
489
XRServer *xr_server = XRServer::get_singleton();
490
ERR_FAIL_NULL_V(xr_server, nullptr);
491
ERR_FAIL_NULL_V(openxr_api, nullptr);
492
493
Tracker *tracker = nullptr;
494
for (int i = 0; i < trackers.size(); i++) {
495
tracker = trackers[i];
496
if (tracker->tracker_name == p_tracker_name) {
497
return tracker;
498
}
499
}
500
501
if (!p_create) {
502
return nullptr;
503
}
504
505
ERR_FAIL_COND_V(!openxr_api->is_top_level_path_supported(p_tracker_name), nullptr);
506
507
// Create our RID
508
RID tracker_rid = openxr_api->tracker_create(p_tracker_name);
509
ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr);
510
511
// Create our controller tracker.
512
Ref<XRControllerTracker> controller_tracker;
513
controller_tracker.instantiate();
514
515
// We have standardized some names to make things nicer to the user so lets recognize the toplevel paths related to these.
516
if (p_tracker_name == "/user/hand/left") {
517
controller_tracker->set_tracker_name("left_hand");
518
controller_tracker->set_tracker_desc("Left hand controller");
519
controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_LEFT);
520
} else if (p_tracker_name == "/user/hand/right") {
521
controller_tracker->set_tracker_name("right_hand");
522
controller_tracker->set_tracker_desc("Right hand controller");
523
controller_tracker->set_tracker_hand(XRPositionalTracker::TRACKER_HAND_RIGHT);
524
} else {
525
controller_tracker->set_tracker_name(p_tracker_name);
526
controller_tracker->set_tracker_desc(p_tracker_name);
527
}
528
controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
529
xr_server->add_tracker(controller_tracker);
530
531
// create a new entry
532
tracker = memnew(Tracker);
533
tracker->tracker_name = p_tracker_name;
534
tracker->tracker_rid = tracker_rid;
535
tracker->controller_tracker = controller_tracker;
536
tracker->interaction_profile = RID();
537
trackers.push_back(tracker);
538
539
return tracker;
540
}
541
542
void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_profile) {
543
Tracker *tracker = nullptr;
544
for (int i = 0; i < trackers.size() && tracker == nullptr; i++) {
545
if (trackers[i]->tracker_rid == p_tracker) {
546
tracker = trackers[i];
547
}
548
}
549
ERR_FAIL_NULL(tracker);
550
551
tracker->interaction_profile = p_interaction_profile;
552
553
if (p_interaction_profile.is_null()) {
554
print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + INTERACTION_PROFILE_NONE);
555
tracker->controller_tracker->set_tracker_profile(INTERACTION_PROFILE_NONE);
556
} else {
557
String name = openxr_api->interaction_profile_get_name(p_interaction_profile);
558
print_verbose("OpenXR: Interaction profile for " + tracker->tracker_name + " changed to " + name);
559
tracker->controller_tracker->set_tracker_profile(name);
560
}
561
}
562
563
void OpenXRInterface::handle_tracker(Tracker *p_tracker) {
564
ERR_FAIL_NULL(openxr_api);
565
ERR_FAIL_COND(p_tracker->controller_tracker.is_null());
566
567
// Note, which actions are actually bound to inputs are handled by our interaction profiles however interaction
568
// profiles are suggested bindings for controller types we know about. OpenXR runtimes can stray away from these
569
// and rebind them or even offer bindings to controllers that are not known to us.
570
571
// We don't really have a consistent way to detect whether a controller is active however as long as it is
572
// unbound it seems to be unavailable, so far unknown controller seem to mimic one of the profiles we've
573
// supplied.
574
if (p_tracker->interaction_profile.is_null()) {
575
return;
576
}
577
578
// We check all actions that are related to our tracker.
579
for (int i = 0; i < p_tracker->actions.size(); i++) {
580
Action *action = p_tracker->actions[i];
581
switch (action->action_type) {
582
case OpenXRAction::OPENXR_ACTION_BOOL: {
583
bool pressed = openxr_api->get_action_bool(action->action_rid, p_tracker->tracker_rid);
584
p_tracker->controller_tracker->set_input(action->action_name, Variant(pressed));
585
} break;
586
case OpenXRAction::OPENXR_ACTION_FLOAT: {
587
real_t value = openxr_api->get_action_float(action->action_rid, p_tracker->tracker_rid);
588
p_tracker->controller_tracker->set_input(action->action_name, Variant(value));
589
} break;
590
case OpenXRAction::OPENXR_ACTION_VECTOR2: {
591
Vector2 value = openxr_api->get_action_vector2(action->action_rid, p_tracker->tracker_rid);
592
p_tracker->controller_tracker->set_input(action->action_name, Variant(value));
593
} break;
594
case OpenXRAction::OPENXR_ACTION_POSE: {
595
Transform3D transform;
596
Vector3 linear, angular;
597
598
XRPose::TrackingConfidence confidence = openxr_api->get_action_pose(action->action_rid, p_tracker->tracker_rid, transform, linear, angular);
599
600
if (confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
601
p_tracker->controller_tracker->set_pose(action->action_name, transform, linear, angular, confidence);
602
} else {
603
p_tracker->controller_tracker->invalidate_pose(action->action_name);
604
}
605
} break;
606
default: {
607
// not yet supported
608
} break;
609
}
610
}
611
}
612
613
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) {
614
ERR_FAIL_NULL(openxr_api);
615
616
Action *action = find_action(p_action_name);
617
ERR_FAIL_NULL(action);
618
619
// We need to map our tracker name to our OpenXR name for our inbuild names.
620
String tracker_name = p_tracker_name;
621
if (tracker_name == "left_hand") {
622
tracker_name = "/user/hand/left";
623
} else if (tracker_name == "right_hand") {
624
tracker_name = "/user/hand/right";
625
}
626
Tracker *tracker = find_tracker(tracker_name);
627
ERR_FAIL_NULL(tracker);
628
629
// TODO OpenXR does not support delay, so we may need to add support for that somehow...
630
631
XrDuration duration = XrDuration(p_duration_sec * 1000000000.0); // seconds -> nanoseconds
632
633
openxr_api->trigger_haptic_pulse(action->action_rid, tracker->tracker_rid, p_frequency, p_amplitude, duration);
634
}
635
636
void OpenXRInterface::free_trackers() {
637
XRServer *xr_server = XRServer::get_singleton();
638
ERR_FAIL_NULL(xr_server);
639
ERR_FAIL_NULL(openxr_api);
640
641
for (int i = 0; i < trackers.size(); i++) {
642
Tracker *tracker = trackers[i];
643
644
openxr_api->tracker_free(tracker->tracker_rid);
645
xr_server->remove_tracker(tracker->controller_tracker);
646
tracker->controller_tracker.unref();
647
648
memdelete(tracker);
649
}
650
trackers.clear();
651
}
652
653
void OpenXRInterface::free_interaction_profiles() {
654
ERR_FAIL_NULL(openxr_api);
655
656
for (const RID &interaction_profile : interaction_profiles) {
657
openxr_api->interaction_profile_free(interaction_profile);
658
}
659
interaction_profiles.clear();
660
}
661
662
bool OpenXRInterface::initialize_on_startup() const {
663
if (openxr_api == nullptr) {
664
return false;
665
} else if (!openxr_api->is_initialized()) {
666
return false;
667
} else {
668
return true;
669
}
670
}
671
672
bool OpenXRInterface::is_initialized() const {
673
return initialized;
674
}
675
676
bool OpenXRInterface::initialize() {
677
XRServer *xr_server = XRServer::get_singleton();
678
ERR_FAIL_NULL_V(xr_server, false);
679
680
if (openxr_api == nullptr) {
681
return false;
682
} else if (!openxr_api->is_initialized()) {
683
return false;
684
} else if (initialized) {
685
return true;
686
}
687
688
// load up our action sets before setting up our session, note that our profiles are suggestions, OpenXR takes ownership of (re)binding
689
_load_action_map();
690
691
if (!openxr_api->initialize_session()) {
692
return false;
693
}
694
695
// we must create a tracker for our head
696
head.instantiate();
697
head->set_tracker_type(XRServer::TRACKER_HEAD);
698
head->set_tracker_name("head");
699
head->set_tracker_desc("Players head");
700
xr_server->add_tracker(head);
701
702
// attach action sets
703
Vector<RID> loaded_action_sets;
704
for (int i = 0; i < action_sets.size(); i++) {
705
loaded_action_sets.append(action_sets[i]->action_set_rid);
706
}
707
openxr_api->attach_action_sets(loaded_action_sets);
708
709
// make this our primary interface
710
xr_server->set_primary_interface(this);
711
712
// Register an additional output with the display server, so rendering won't
713
// be skipped if no windows are visible.
714
DisplayServer::get_singleton()->register_additional_output(this);
715
716
initialized = true;
717
718
return initialized;
719
}
720
721
void OpenXRInterface::uninitialize() {
722
// Our OpenXR driver will clean itself up properly when Godot exits, so we just do some basic stuff here
723
724
// end the session if we need to?
725
726
// cleanup stuff
727
free_trackers();
728
free_interaction_profiles();
729
free_action_sets();
730
731
XRServer *xr_server = XRServer::get_singleton();
732
if (xr_server) {
733
if (head.is_valid()) {
734
xr_server->remove_tracker(head);
735
head.unref();
736
}
737
}
738
739
DisplayServer::get_singleton()->unregister_additional_output(this);
740
741
initialized = false;
742
}
743
744
Dictionary OpenXRInterface::get_system_info() {
745
Dictionary dict;
746
747
if (openxr_api) {
748
dict[SNAME("XRRuntimeName")] = openxr_api->get_runtime_name();
749
dict[SNAME("XRRuntimeVersion")] = openxr_api->get_runtime_version();
750
dict[SNAME("OpenXRSystemName")] = openxr_api->get_system_name();
751
dict[SNAME("OpenXRVendorID")] = openxr_api->get_vendor_id();
752
}
753
754
return dict;
755
}
756
757
bool OpenXRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
758
if (p_mode == XRInterface::XR_PLAY_AREA_3DOF) {
759
return false;
760
}
761
return true;
762
}
763
764
XRInterface::PlayAreaMode OpenXRInterface::get_play_area_mode() const {
765
if (!openxr_api || !initialized) {
766
return XRInterface::XR_PLAY_AREA_UNKNOWN;
767
}
768
769
XrReferenceSpaceType reference_space = openxr_api->get_reference_space();
770
771
if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL) {
772
return XRInterface::XR_PLAY_AREA_SITTING;
773
} else if (reference_space == XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT) {
774
return XRInterface::XR_PLAY_AREA_ROOMSCALE;
775
} else if (reference_space == XR_REFERENCE_SPACE_TYPE_STAGE) {
776
return XRInterface::XR_PLAY_AREA_STAGE;
777
} else if (reference_space == XR_REFERENCE_SPACE_TYPE_MAX_ENUM) {
778
return XRInterface::XR_PLAY_AREA_CUSTOM;
779
}
780
781
return XRInterface::XR_PLAY_AREA_UNKNOWN;
782
}
783
784
bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
785
ERR_FAIL_NULL_V(openxr_api, false);
786
787
XrReferenceSpaceType reference_space;
788
789
if (p_mode == XRInterface::XR_PLAY_AREA_SITTING) {
790
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL;
791
} else if (p_mode == XRInterface::XR_PLAY_AREA_ROOMSCALE) {
792
reference_space = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
793
} else if (p_mode == XRInterface::XR_PLAY_AREA_STAGE) {
794
reference_space = XR_REFERENCE_SPACE_TYPE_STAGE;
795
} else {
796
return false;
797
}
798
799
if (openxr_api->set_requested_reference_space(reference_space)) {
800
XRServer *xr_server = XRServer::get_singleton();
801
if (xr_server) {
802
xr_server->clear_reference_frame();
803
}
804
return true;
805
}
806
807
return false;
808
}
809
810
PackedVector3Array OpenXRInterface::get_play_area() const {
811
XRServer *xr_server = XRServer::get_singleton();
812
ERR_FAIL_NULL_V(xr_server, PackedVector3Array());
813
PackedVector3Array arr;
814
815
Vector3 sides[4] = {
816
Vector3(-0.5f, 0.0f, -0.5f),
817
Vector3(0.5f, 0.0f, -0.5f),
818
Vector3(0.5f, 0.0f, 0.5f),
819
Vector3(-0.5f, 0.0f, 0.5f),
820
};
821
822
if (openxr_api != nullptr && openxr_api->is_initialized()) {
823
Size2 extents = openxr_api->get_play_space_bounds();
824
if (extents.width != 0.0 && extents.height != 0.0) {
825
Transform3D reference_frame = xr_server->get_reference_frame();
826
827
for (int i = 0; i < 4; i++) {
828
Vector3 coord = sides[i];
829
830
// Scale it up.
831
coord.x *= extents.width;
832
coord.z *= extents.height;
833
834
// Now apply our reference.
835
Vector3 out = reference_frame.xform(coord);
836
arr.push_back(out);
837
}
838
} else {
839
WARN_PRINT_ONCE("OpenXR: No extents available.");
840
}
841
}
842
843
return arr;
844
}
845
846
float OpenXRInterface::get_display_refresh_rate() const {
847
if (openxr_api == nullptr) {
848
return 0.0;
849
} else if (!openxr_api->is_initialized()) {
850
return 0.0;
851
} else {
852
return openxr_api->get_display_refresh_rate();
853
}
854
}
855
856
void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) {
857
if (openxr_api == nullptr) {
858
return;
859
} else if (!openxr_api->is_initialized()) {
860
return;
861
} else {
862
openxr_api->set_display_refresh_rate(p_refresh_rate);
863
}
864
}
865
866
Array OpenXRInterface::get_available_display_refresh_rates() const {
867
if (openxr_api == nullptr) {
868
return Array();
869
} else if (!openxr_api->is_initialized()) {
870
return Array();
871
} else {
872
return openxr_api->get_available_display_refresh_rates();
873
}
874
}
875
876
bool OpenXRInterface::is_hand_tracking_supported() {
877
if (openxr_api == nullptr) {
878
return false;
879
} else if (!openxr_api->is_initialized()) {
880
return false;
881
} else {
882
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
883
if (hand_tracking_ext == nullptr) {
884
return false;
885
} else {
886
return hand_tracking_ext->get_active();
887
}
888
}
889
}
890
891
bool OpenXRInterface::is_hand_interaction_supported() const {
892
if (openxr_api == nullptr) {
893
return false;
894
} else if (!openxr_api->is_initialized()) {
895
return false;
896
} else {
897
OpenXRHandInteractionExtension *hand_interaction_ext = OpenXRHandInteractionExtension::get_singleton();
898
if (hand_interaction_ext == nullptr) {
899
return false;
900
} else {
901
return hand_interaction_ext->is_available();
902
}
903
}
904
}
905
906
bool OpenXRInterface::is_eye_gaze_interaction_supported() {
907
if (openxr_api == nullptr) {
908
return false;
909
} else if (!openxr_api->is_initialized()) {
910
return false;
911
} else {
912
OpenXREyeGazeInteractionExtension *eye_gaze_ext = OpenXREyeGazeInteractionExtension::get_singleton();
913
if (eye_gaze_ext == nullptr) {
914
return false;
915
} else {
916
return eye_gaze_ext->supports_eye_gaze_interaction();
917
}
918
}
919
}
920
921
bool OpenXRInterface::is_action_set_active(const String &p_action_set) const {
922
for (ActionSet *action_set : action_sets) {
923
if (action_set->action_set_name == p_action_set) {
924
return action_set->is_active;
925
}
926
}
927
928
WARN_PRINT("OpenXR: Unknown action set " + p_action_set);
929
return false;
930
}
931
932
void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) {
933
for (ActionSet *action_set : action_sets) {
934
if (action_set->action_set_name == p_action_set) {
935
action_set->is_active = p_active;
936
return;
937
}
938
}
939
940
WARN_PRINT("OpenXR: Unknown action set " + p_action_set);
941
}
942
943
Array OpenXRInterface::get_action_sets() const {
944
Array arr;
945
946
for (ActionSet *action_set : action_sets) {
947
arr.push_back(action_set->action_set_name);
948
}
949
950
return arr;
951
}
952
953
float OpenXRInterface::get_vrs_min_radius() const {
954
return xr_vrs.get_vrs_min_radius();
955
}
956
957
void OpenXRInterface::set_vrs_min_radius(float p_vrs_min_radius) {
958
xr_vrs.set_vrs_min_radius(p_vrs_min_radius);
959
}
960
961
float OpenXRInterface::get_vrs_strength() const {
962
return xr_vrs.get_vrs_strength();
963
}
964
965
void OpenXRInterface::set_vrs_strength(float p_vrs_strength) {
966
xr_vrs.set_vrs_strength(p_vrs_strength);
967
}
968
969
double OpenXRInterface::get_render_target_size_multiplier() const {
970
if (openxr_api == nullptr) {
971
return 1.0;
972
} else {
973
return openxr_api->get_render_target_size_multiplier();
974
}
975
}
976
977
void OpenXRInterface::set_render_target_size_multiplier(double multiplier) {
978
if (openxr_api == nullptr) {
979
return;
980
} else {
981
openxr_api->set_render_target_size_multiplier(multiplier);
982
}
983
}
984
985
bool OpenXRInterface::is_foveation_supported() const {
986
if (openxr_api == nullptr) {
987
return false;
988
} else {
989
return openxr_api->is_foveation_supported();
990
}
991
}
992
993
int OpenXRInterface::get_foveation_level() const {
994
if (openxr_api == nullptr) {
995
return 0;
996
} else {
997
return openxr_api->get_foveation_level();
998
}
999
}
1000
1001
void OpenXRInterface::set_foveation_level(int p_foveation_level) {
1002
if (openxr_api == nullptr) {
1003
return;
1004
} else {
1005
openxr_api->set_foveation_level(p_foveation_level);
1006
}
1007
}
1008
1009
bool OpenXRInterface::get_foveation_dynamic() const {
1010
if (openxr_api == nullptr) {
1011
return false;
1012
} else {
1013
return openxr_api->get_foveation_dynamic();
1014
}
1015
}
1016
1017
void OpenXRInterface::set_foveation_dynamic(bool p_foveation_dynamic) {
1018
if (openxr_api == nullptr) {
1019
return;
1020
} else {
1021
openxr_api->set_foveation_dynamic(p_foveation_dynamic);
1022
}
1023
}
1024
1025
Size2 OpenXRInterface::get_render_target_size() {
1026
if (openxr_api == nullptr) {
1027
return Size2();
1028
} else {
1029
return openxr_api->get_recommended_target_size();
1030
}
1031
}
1032
1033
uint32_t OpenXRInterface::get_view_count() {
1034
// TODO set this based on our configuration
1035
return 2;
1036
}
1037
1038
void OpenXRInterface::_set_default_pos(Transform3D &r_transform, double p_world_scale, uint64_t p_eye) {
1039
r_transform = Transform3D();
1040
1041
// if we're not tracking, don't put our head on the floor...
1042
r_transform.origin.y = 1.5 * p_world_scale;
1043
1044
// overkill but..
1045
if (p_eye == 1) {
1046
r_transform.origin.x = 0.03 * p_world_scale;
1047
} else if (p_eye == 2) {
1048
r_transform.origin.x = -0.03 * p_world_scale;
1049
}
1050
}
1051
1052
Transform3D OpenXRInterface::get_camera_transform() {
1053
XRServer *xr_server = XRServer::get_singleton();
1054
ERR_FAIL_NULL_V(xr_server, Transform3D());
1055
1056
Transform3D hmd_transform;
1057
double world_scale = xr_server->get_world_scale();
1058
1059
// head_transform should be updated in process
1060
1061
hmd_transform.basis = head_transform.basis;
1062
hmd_transform.origin = head_transform.origin * world_scale;
1063
1064
return hmd_transform;
1065
}
1066
1067
Transform3D OpenXRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
1068
XRServer *xr_server = XRServer::get_singleton();
1069
ERR_FAIL_NULL_V(xr_server, Transform3D());
1070
ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), Transform3D(), "View index outside bounds.");
1071
1072
Transform3D t;
1073
if (openxr_api && openxr_api->get_view_transform(p_view, t)) {
1074
// update our cached value if we have a valid transform
1075
transform_for_view[p_view] = t;
1076
} else {
1077
// reuse cached value
1078
t = transform_for_view[p_view];
1079
}
1080
1081
// Apply our world scale
1082
double world_scale = xr_server->get_world_scale();
1083
t.origin *= world_scale;
1084
1085
return p_cam_transform * xr_server->get_reference_frame() * t;
1086
}
1087
1088
Projection OpenXRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
1089
Projection cm;
1090
ERR_FAIL_UNSIGNED_INDEX_V_MSG(p_view, get_view_count(), cm, "View index outside bounds.");
1091
1092
if (openxr_api) {
1093
if (openxr_api->get_view_projection(p_view, p_z_near, p_z_far, cm)) {
1094
return cm;
1095
}
1096
}
1097
1098
// Failed to get from our OpenXR device? Default to some sort of sensible camera matrix..
1099
cm.set_for_hmd(p_view + 1, 1.0, 6.0, 14.5, 4.0, 1.5, p_z_near, p_z_far);
1100
1101
return cm;
1102
}
1103
1104
Rect2i OpenXRInterface::get_render_region() {
1105
if (openxr_api) {
1106
return openxr_api->get_render_region();
1107
} else {
1108
return Rect2i();
1109
}
1110
}
1111
1112
RID OpenXRInterface::get_color_texture() {
1113
if (openxr_api) {
1114
return openxr_api->get_color_texture();
1115
} else {
1116
return RID();
1117
}
1118
}
1119
1120
RID OpenXRInterface::get_depth_texture() {
1121
if (openxr_api) {
1122
return openxr_api->get_depth_texture();
1123
} else {
1124
return RID();
1125
}
1126
}
1127
1128
RID OpenXRInterface::get_velocity_texture() {
1129
if (openxr_api) {
1130
return openxr_api->get_velocity_texture();
1131
} else {
1132
return RID();
1133
}
1134
}
1135
1136
RID OpenXRInterface::get_velocity_depth_texture() {
1137
if (openxr_api) {
1138
return openxr_api->get_velocity_depth_texture();
1139
} else {
1140
return RID();
1141
}
1142
}
1143
1144
Size2i OpenXRInterface::get_velocity_target_size() {
1145
if (openxr_api) {
1146
return openxr_api->get_velocity_target_size();
1147
} else {
1148
return Size2i();
1149
}
1150
}
1151
1152
void OpenXRInterface::handle_hand_tracking(const String &p_path, OpenXRHandTrackingExtension::HandTrackedHands p_hand) {
1153
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1154
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1155
OpenXRInterface::Tracker *tracker = find_tracker(p_path);
1156
if (tracker && tracker->controller_tracker.is_valid()) {
1157
XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);
1158
1159
if (location_flags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT + XR_SPACE_LOCATION_POSITION_VALID_BIT)) {
1160
static 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;
1161
XRPose::TrackingConfidence confidence = XRPose::XR_TRACKING_CONFIDENCE_LOW;
1162
Transform3D transform;
1163
Vector3 linear_velocity;
1164
Vector3 angular_velocity;
1165
1166
if ((location_flags & all_location_flags) == all_location_flags) {
1167
// All flags set? confidence is high!
1168
confidence = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
1169
}
1170
1171
if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
1172
transform.basis = Basis(hand_tracking_ext->get_hand_joint_rotation(p_hand, XR_HAND_JOINT_PALM_EXT));
1173
}
1174
if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
1175
transform.origin = hand_tracking_ext->get_hand_joint_position(p_hand, XR_HAND_JOINT_PALM_EXT);
1176
}
1177
1178
XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_location_flags(p_hand, XR_HAND_JOINT_PALM_EXT);
1179
if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
1180
linear_velocity = hand_tracking_ext->get_hand_joint_linear_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
1181
}
1182
if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
1183
angular_velocity = hand_tracking_ext->get_hand_joint_angular_velocity(p_hand, XR_HAND_JOINT_PALM_EXT);
1184
}
1185
1186
tracker->controller_tracker->set_pose("skeleton", transform, linear_velocity, angular_velocity, confidence);
1187
} else {
1188
tracker->controller_tracker->invalidate_pose("skeleton");
1189
}
1190
}
1191
}
1192
}
1193
1194
void OpenXRInterface::process() {
1195
if (openxr_api) {
1196
// do our normal process
1197
if (openxr_api->process()) {
1198
Transform3D t;
1199
Vector3 linear_velocity;
1200
Vector3 angular_velocity;
1201
head_confidence = openxr_api->get_head_center(t, linear_velocity, angular_velocity);
1202
if (head_confidence != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
1203
// Only update our transform if we have one to update it with
1204
// note that poses are stored without world scale and reference frame applied!
1205
head_transform = t;
1206
head_linear_velocity = linear_velocity;
1207
head_angular_velocity = angular_velocity;
1208
}
1209
}
1210
1211
// handle our action sets....
1212
Vector<RID> active_sets;
1213
for (int i = 0; i < action_sets.size(); i++) {
1214
if (action_sets[i]->is_active) {
1215
active_sets.push_back(action_sets[i]->action_set_rid);
1216
}
1217
}
1218
1219
if (openxr_api->sync_action_sets(active_sets)) {
1220
for (int i = 0; i < trackers.size(); i++) {
1221
handle_tracker(trackers[i]);
1222
}
1223
}
1224
1225
// Handle hand tracking
1226
handle_hand_tracking("/user/hand/left", OpenXRHandTrackingExtension::OPENXR_TRACKED_LEFT_HAND);
1227
handle_hand_tracking("/user/hand/right", OpenXRHandTrackingExtension::OPENXR_TRACKED_RIGHT_HAND);
1228
}
1229
1230
if (head.is_valid()) {
1231
head->set_pose("default", head_transform, head_linear_velocity, head_angular_velocity, head_confidence);
1232
}
1233
1234
if (reference_stage_changing) {
1235
// Now that we have updated tracking information in our updated reference space, trigger our pose recentered signal.
1236
emit_signal(SNAME("pose_recentered"));
1237
reference_stage_changing = false;
1238
}
1239
}
1240
1241
void OpenXRInterface::pre_render() {
1242
if (openxr_api) {
1243
openxr_api->pre_render();
1244
}
1245
}
1246
1247
bool OpenXRInterface::pre_draw_viewport(RID p_render_target) {
1248
if (openxr_api) {
1249
return openxr_api->pre_draw_viewport(p_render_target);
1250
} else {
1251
// don't render
1252
return false;
1253
}
1254
}
1255
1256
Vector<BlitToScreen> OpenXRInterface::post_draw_viewport(RID p_render_target, const Rect2 &p_screen_rect) {
1257
Vector<BlitToScreen> blit_to_screen;
1258
1259
#ifndef ANDROID_ENABLED
1260
// If separate HMD we should output one eye to screen
1261
if (p_screen_rect != Rect2()) {
1262
BlitToScreen blit;
1263
1264
blit.render_target = p_render_target;
1265
blit.multi_view.use_layer = true;
1266
blit.multi_view.layer = 0;
1267
blit.lens_distortion.apply = false;
1268
1269
Size2 render_size = get_render_target_size();
1270
Rect2 dst_rect = p_screen_rect;
1271
float new_height = dst_rect.size.x * (render_size.y / render_size.x);
1272
if (new_height > dst_rect.size.y) {
1273
dst_rect.position.y = (0.5 * dst_rect.size.y) - (0.5 * new_height);
1274
dst_rect.size.y = new_height;
1275
} else {
1276
float new_width = dst_rect.size.y * (render_size.x / render_size.y);
1277
1278
dst_rect.position.x = (0.5 * dst_rect.size.x) - (0.5 * new_width);
1279
dst_rect.size.x = new_width;
1280
}
1281
1282
blit.dst_rect = dst_rect;
1283
blit_to_screen.push_back(blit);
1284
}
1285
#endif
1286
1287
if (openxr_api) {
1288
openxr_api->post_draw_viewport(p_render_target);
1289
}
1290
1291
return blit_to_screen;
1292
}
1293
1294
void OpenXRInterface::end_frame() {
1295
if (openxr_api) {
1296
openxr_api->end_frame();
1297
}
1298
}
1299
1300
bool OpenXRInterface::is_passthrough_supported() {
1301
return get_supported_environment_blend_modes().find(XR_ENV_BLEND_MODE_ALPHA_BLEND);
1302
}
1303
1304
bool OpenXRInterface::is_passthrough_enabled() {
1305
return get_environment_blend_mode() == XR_ENV_BLEND_MODE_ALPHA_BLEND;
1306
}
1307
1308
bool OpenXRInterface::start_passthrough() {
1309
return set_environment_blend_mode(XR_ENV_BLEND_MODE_ALPHA_BLEND);
1310
}
1311
1312
void OpenXRInterface::stop_passthrough() {
1313
set_environment_blend_mode(XR_ENV_BLEND_MODE_OPAQUE);
1314
}
1315
1316
Array OpenXRInterface::get_supported_environment_blend_modes() {
1317
if (!openxr_api) {
1318
return Array();
1319
}
1320
Array modes;
1321
1322
const Vector<XrEnvironmentBlendMode> env_blend_modes = openxr_api->get_supported_environment_blend_modes();
1323
1324
for (const XrEnvironmentBlendMode &env_blend_mode : env_blend_modes) {
1325
switch (env_blend_mode) {
1326
case XR_ENVIRONMENT_BLEND_MODE_OPAQUE:
1327
modes.push_back(XR_ENV_BLEND_MODE_OPAQUE);
1328
break;
1329
case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE:
1330
modes.push_back(XR_ENV_BLEND_MODE_ADDITIVE);
1331
break;
1332
case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND:
1333
modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);
1334
break;
1335
default:
1336
WARN_PRINT(vformat("Unsupported blend mode found: %s.", String::num_int64(int64_t(env_blend_mode))));
1337
}
1338
}
1339
1340
if (openxr_api->is_environment_blend_mode_alpha_blend_supported() == OpenXRAPI::OPENXR_ALPHA_BLEND_MODE_SUPPORT_EMULATING) {
1341
modes.push_back(XR_ENV_BLEND_MODE_ALPHA_BLEND);
1342
}
1343
1344
return modes;
1345
}
1346
1347
XRInterface::EnvironmentBlendMode OpenXRInterface::get_environment_blend_mode() const {
1348
if (openxr_api) {
1349
XrEnvironmentBlendMode oxr_blend_mode = openxr_api->get_environment_blend_mode();
1350
switch (oxr_blend_mode) {
1351
case XR_ENVIRONMENT_BLEND_MODE_OPAQUE: {
1352
return XR_ENV_BLEND_MODE_OPAQUE;
1353
} break;
1354
case XR_ENVIRONMENT_BLEND_MODE_ADDITIVE: {
1355
return XR_ENV_BLEND_MODE_ADDITIVE;
1356
} break;
1357
case XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND: {
1358
return XR_ENV_BLEND_MODE_ALPHA_BLEND;
1359
} break;
1360
default:
1361
break;
1362
}
1363
}
1364
1365
return XR_ENV_BLEND_MODE_OPAQUE;
1366
}
1367
1368
bool OpenXRInterface::set_environment_blend_mode(XRInterface::EnvironmentBlendMode mode) {
1369
if (openxr_api) {
1370
XrEnvironmentBlendMode oxr_blend_mode;
1371
switch (mode) {
1372
case XR_ENV_BLEND_MODE_OPAQUE:
1373
oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
1374
break;
1375
case XR_ENV_BLEND_MODE_ADDITIVE:
1376
oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ADDITIVE;
1377
break;
1378
case XR_ENV_BLEND_MODE_ALPHA_BLEND:
1379
oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND;
1380
break;
1381
default:
1382
WARN_PRINT("Unknown blend mode requested: " + String::num_int64(int64_t(mode)));
1383
oxr_blend_mode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
1384
}
1385
return openxr_api->set_environment_blend_mode(oxr_blend_mode);
1386
}
1387
return false;
1388
}
1389
1390
void OpenXRInterface::on_state_ready() {
1391
emit_signal(SNAME("session_begun"));
1392
}
1393
1394
void OpenXRInterface::on_state_visible() {
1395
emit_signal(SNAME("session_visible"));
1396
}
1397
1398
void OpenXRInterface::on_state_synchronized() {
1399
emit_signal(SNAME("session_synchronized"));
1400
}
1401
1402
void OpenXRInterface::on_state_focused() {
1403
emit_signal(SNAME("session_focussed"));
1404
}
1405
1406
void OpenXRInterface::on_state_stopping() {
1407
emit_signal(SNAME("session_stopping"));
1408
}
1409
1410
void OpenXRInterface::on_state_loss_pending() {
1411
emit_signal(SNAME("session_loss_pending"));
1412
}
1413
1414
void OpenXRInterface::on_state_exiting() {
1415
emit_signal(SNAME("instance_exiting"));
1416
}
1417
1418
void OpenXRInterface::on_reference_space_change_pending(XrReferenceSpaceType p_type) {
1419
reference_stage_changing = true;
1420
1421
// Emit play area bounds changed signal when the reference space changes.
1422
PlayAreaMode mode = XR_PLAY_AREA_UNKNOWN;
1423
1424
switch (p_type) {
1425
case XR_REFERENCE_SPACE_TYPE_VIEW:
1426
mode = XR_PLAY_AREA_3DOF;
1427
break;
1428
case XR_REFERENCE_SPACE_TYPE_LOCAL:
1429
mode = XR_PLAY_AREA_SITTING;
1430
break;
1431
case XR_REFERENCE_SPACE_TYPE_STAGE:
1432
mode = XR_PLAY_AREA_STAGE;
1433
break;
1434
case XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR:
1435
mode = XR_PLAY_AREA_ROOMSCALE;
1436
break;
1437
default:
1438
mode = XR_PLAY_AREA_UNKNOWN;
1439
break;
1440
}
1441
1442
print_verbose("OpenXR Interface: Play area changed, emitting signal.");
1443
emit_signal(SNAME("play_area_changed"), mode);
1444
}
1445
1446
void OpenXRInterface::on_refresh_rate_changes(float p_new_rate) {
1447
emit_signal(SNAME("refresh_rate_changed"), p_new_rate);
1448
}
1449
1450
OpenXRInterface::SessionState OpenXRInterface::get_session_state() {
1451
if (openxr_api) {
1452
return (SessionState)openxr_api->get_session_state();
1453
}
1454
1455
return SESSION_STATE_UNKNOWN;
1456
}
1457
1458
/** User Presence. */
1459
bool OpenXRInterface::is_user_presence_supported() const {
1460
if (!openxr_api || !openxr_api->is_initialized()) {
1461
return false;
1462
} else {
1463
OpenXRUserPresenceExtension *user_presence_ext = OpenXRUserPresenceExtension::get_singleton();
1464
return user_presence_ext && user_presence_ext->is_active();
1465
}
1466
}
1467
1468
bool OpenXRInterface::is_user_present() const {
1469
// If extension is unavailable or unsupported, we default to user is present.
1470
if (!is_user_presence_supported()) {
1471
return true;
1472
} else {
1473
OpenXRUserPresenceExtension *user_presence_ext = OpenXRUserPresenceExtension::get_singleton();
1474
return user_presence_ext->is_user_present();
1475
}
1476
}
1477
1478
/** Hand tracking. */
1479
void OpenXRInterface::set_motion_range(const Hand p_hand, const HandMotionRange p_motion_range) {
1480
ERR_FAIL_INDEX(p_hand, HAND_MAX);
1481
ERR_FAIL_INDEX(p_motion_range, HAND_MOTION_RANGE_MAX);
1482
1483
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1484
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1485
XrHandJointsMotionRangeEXT xr_motion_range;
1486
switch (p_motion_range) {
1487
case HAND_MOTION_RANGE_UNOBSTRUCTED:
1488
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
1489
break;
1490
case HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER:
1491
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
1492
break;
1493
default:
1494
// Shouldn't get here, ERR_FAIL_INDEX should have caught this...
1495
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
1496
break;
1497
}
1498
1499
hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), xr_motion_range);
1500
}
1501
}
1502
1503
OpenXRInterface::HandMotionRange OpenXRInterface::get_motion_range(const Hand p_hand) const {
1504
ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_MOTION_RANGE_MAX);
1505
1506
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1507
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1508
XrHandJointsMotionRangeEXT xr_motion_range = hand_tracking_ext->get_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));
1509
1510
switch (xr_motion_range) {
1511
case XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT:
1512
return HAND_MOTION_RANGE_UNOBSTRUCTED;
1513
case XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT:
1514
return HAND_MOTION_RANGE_CONFORM_TO_CONTROLLER;
1515
default:
1516
ERR_FAIL_V_MSG(HAND_MOTION_RANGE_MAX, "Unknown motion range returned by OpenXR");
1517
}
1518
}
1519
1520
return HAND_MOTION_RANGE_MAX;
1521
}
1522
1523
OpenXRInterface::HandTrackedSource OpenXRInterface::get_hand_tracking_source(const Hand p_hand) const {
1524
ERR_FAIL_INDEX_V(p_hand, HAND_MAX, HAND_TRACKED_SOURCE_UNKNOWN);
1525
1526
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1527
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1528
OpenXRHandTrackingExtension::HandTrackedSource source = hand_tracking_ext->get_hand_tracking_source(OpenXRHandTrackingExtension::HandTrackedHands(p_hand));
1529
switch (source) {
1530
case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNOBSTRUCTED:
1531
return HAND_TRACKED_SOURCE_UNOBSTRUCTED;
1532
case OpenXRHandTrackingExtension::OPENXR_SOURCE_CONTROLLER:
1533
return HAND_TRACKED_SOURCE_CONTROLLER;
1534
case OpenXRHandTrackingExtension::OPENXR_SOURCE_UNKNOWN:
1535
case OpenXRHandTrackingExtension::OPENXR_SOURCE_NOT_TRACKED:
1536
return HAND_TRACKED_SOURCE_UNKNOWN;
1537
default:
1538
ERR_FAIL_V_MSG(HAND_TRACKED_SOURCE_UNKNOWN, "Unknown hand tracking source (" + String::num_int64(source) + ") returned by OpenXR");
1539
}
1540
}
1541
1542
return HAND_TRACKED_SOURCE_UNKNOWN;
1543
}
1544
1545
BitField<OpenXRInterface::HandJointFlags> OpenXRInterface::get_hand_joint_flags(Hand p_hand, HandJoints p_joint) const {
1546
BitField<OpenXRInterface::HandJointFlags> bits = HAND_JOINT_NONE;
1547
1548
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1549
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1550
XrSpaceLocationFlags location_flags = hand_tracking_ext->get_hand_joint_location_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1551
if (location_flags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
1552
bits.set_flag(HAND_JOINT_ORIENTATION_VALID);
1553
}
1554
if (location_flags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
1555
bits.set_flag(HAND_JOINT_ORIENTATION_TRACKED);
1556
}
1557
if (location_flags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
1558
bits.set_flag(HAND_JOINT_POSITION_VALID);
1559
}
1560
if (location_flags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
1561
bits.set_flag(HAND_JOINT_POSITION_TRACKED);
1562
}
1563
1564
XrSpaceVelocityFlags velocity_flags = hand_tracking_ext->get_hand_joint_velocity_flags(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1565
if (velocity_flags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
1566
bits.set_flag(HAND_JOINT_LINEAR_VELOCITY_VALID);
1567
}
1568
if (velocity_flags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
1569
bits.set_flag(HAND_JOINT_ANGULAR_VELOCITY_VALID);
1570
}
1571
}
1572
1573
return bits;
1574
}
1575
1576
Quaternion OpenXRInterface::get_hand_joint_rotation(Hand p_hand, HandJoints p_joint) const {
1577
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1578
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1579
return hand_tracking_ext->get_hand_joint_rotation(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1580
}
1581
1582
return Quaternion();
1583
}
1584
1585
Vector3 OpenXRInterface::get_hand_joint_position(Hand p_hand, HandJoints p_joint) const {
1586
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1587
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1588
return hand_tracking_ext->get_hand_joint_position(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1589
}
1590
1591
return Vector3();
1592
}
1593
1594
float OpenXRInterface::get_hand_joint_radius(Hand p_hand, HandJoints p_joint) const {
1595
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1596
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1597
return hand_tracking_ext->get_hand_joint_radius(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1598
}
1599
1600
return 0.0;
1601
}
1602
1603
Vector3 OpenXRInterface::get_hand_joint_linear_velocity(Hand p_hand, HandJoints p_joint) const {
1604
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1605
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1606
return hand_tracking_ext->get_hand_joint_linear_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1607
}
1608
1609
return Vector3();
1610
}
1611
1612
Vector3 OpenXRInterface::get_hand_joint_angular_velocity(Hand p_hand, HandJoints p_joint) const {
1613
OpenXRHandTrackingExtension *hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
1614
if (hand_tracking_ext && hand_tracking_ext->get_active()) {
1615
return hand_tracking_ext->get_hand_joint_angular_velocity(OpenXRHandTrackingExtension::HandTrackedHands(p_hand), XrHandJointEXT(p_joint));
1616
}
1617
1618
return Vector3();
1619
}
1620
1621
RID OpenXRInterface::get_vrs_texture() {
1622
if (!openxr_api) {
1623
return RID();
1624
}
1625
1626
RID density_map = openxr_api->get_density_map_texture();
1627
if (density_map.is_valid()) {
1628
return density_map;
1629
}
1630
1631
PackedVector2Array eye_foci;
1632
1633
Size2 target_size = get_render_target_size();
1634
real_t aspect_ratio = target_size.x / target_size.y;
1635
uint32_t view_count = get_view_count();
1636
1637
for (uint32_t v = 0; v < view_count; v++) {
1638
eye_foci.push_back(openxr_api->get_eye_focus(v, aspect_ratio));
1639
}
1640
1641
xr_vrs.set_vrs_render_region(get_render_region());
1642
1643
return xr_vrs.make_vrs_texture(target_size, eye_foci);
1644
}
1645
1646
XRInterface::VRSTextureFormat OpenXRInterface::get_vrs_texture_format() {
1647
if (!openxr_api) {
1648
return XR_VRS_TEXTURE_FORMAT_UNIFIED;
1649
}
1650
1651
RID density_map = openxr_api->get_density_map_texture();
1652
if (density_map.is_valid()) {
1653
return XR_VRS_TEXTURE_FORMAT_FRAGMENT_DENSITY_MAP;
1654
}
1655
1656
return XR_VRS_TEXTURE_FORMAT_UNIFIED;
1657
}
1658
1659
void OpenXRInterface::set_cpu_level(PerfSettingsLevel p_level) {
1660
OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();
1661
if (performance_settings_ext && performance_settings_ext->is_available()) {
1662
performance_settings_ext->set_cpu_level(p_level);
1663
}
1664
}
1665
1666
void OpenXRInterface::set_gpu_level(PerfSettingsLevel p_level) {
1667
OpenXRPerformanceSettingsExtension *performance_settings_ext = OpenXRPerformanceSettingsExtension::get_singleton();
1668
if (performance_settings_ext && performance_settings_ext->is_available()) {
1669
performance_settings_ext->set_gpu_level(p_level);
1670
}
1671
}
1672
1673
void OpenXRInterface::on_cpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {
1674
emit_signal(SNAME("cpu_level_changed"), p_sub_domain, p_from_level, p_to_level);
1675
}
1676
1677
void OpenXRInterface::on_gpu_level_changed(PerfSettingsSubDomain p_sub_domain, PerfSettingsNotificationLevel p_from_level, PerfSettingsNotificationLevel p_to_level) {
1678
emit_signal(SNAME("gpu_level_changed"), p_sub_domain, p_from_level, p_to_level);
1679
}
1680
1681
OpenXRInterface::OpenXRInterface() {
1682
openxr_api = OpenXRAPI::get_singleton();
1683
if (openxr_api) {
1684
openxr_api->set_xr_interface(this);
1685
}
1686
1687
// while we don't have head tracking, don't put the headset on the floor...
1688
_set_default_pos(head_transform, 1.0, 0);
1689
_set_default_pos(transform_for_view[0], 1.0, 1);
1690
_set_default_pos(transform_for_view[1], 1.0, 2);
1691
}
1692
1693
OpenXRInterface::~OpenXRInterface() {
1694
if (is_initialized()) {
1695
uninitialize();
1696
}
1697
1698
if (openxr_api) {
1699
openxr_api->set_xr_interface(nullptr);
1700
openxr_api = nullptr;
1701
}
1702
}
1703
1704