Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/extensions/openxr_hand_tracking_extension.cpp
10278 views
1
/**************************************************************************/
2
/* openxr_hand_tracking_extension.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_hand_tracking_extension.h"
32
33
#include "../openxr_api.h"
34
35
#include "core/config/project_settings.h"
36
#include "core/string/print_string.h"
37
#include "servers/xr_server.h"
38
39
#include <openxr/openxr.h>
40
41
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::singleton = nullptr;
42
43
OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::get_singleton() {
44
return singleton;
45
}
46
47
OpenXRHandTrackingExtension::OpenXRHandTrackingExtension() {
48
singleton = this;
49
50
// Make sure this is cleared until we actually request it
51
handTrackingSystemProperties.supportsHandTracking = false;
52
}
53
54
OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() {
55
singleton = nullptr;
56
}
57
58
HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() {
59
HashMap<String, bool *> request_extensions;
60
61
unobstructed_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_unobstructed_data_source");
62
controller_data_source = GLOBAL_GET("xr/openxr/extensions/hand_tracking_controller_data_source");
63
64
request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext;
65
request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext;
66
if (unobstructed_data_source || controller_data_source) {
67
request_extensions[XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME] = &hand_tracking_source_ext;
68
}
69
70
return request_extensions;
71
}
72
73
void OpenXRHandTrackingExtension::on_instance_created(const XrInstance p_instance) {
74
if (hand_tracking_ext) {
75
EXT_INIT_XR_FUNC(xrCreateHandTrackerEXT);
76
EXT_INIT_XR_FUNC(xrDestroyHandTrackerEXT);
77
EXT_INIT_XR_FUNC(xrLocateHandJointsEXT);
78
79
hand_tracking_ext = xrCreateHandTrackerEXT_ptr && xrDestroyHandTrackerEXT_ptr && xrLocateHandJointsEXT_ptr;
80
}
81
}
82
83
void OpenXRHandTrackingExtension::on_session_destroyed() {
84
cleanup_hand_tracking();
85
}
86
87
void OpenXRHandTrackingExtension::on_instance_destroyed() {
88
xrCreateHandTrackerEXT_ptr = nullptr;
89
xrDestroyHandTrackerEXT_ptr = nullptr;
90
xrLocateHandJointsEXT_ptr = nullptr;
91
}
92
93
void *OpenXRHandTrackingExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) {
94
if (!hand_tracking_ext) {
95
// not supported...
96
return p_next_pointer;
97
}
98
99
handTrackingSystemProperties = {
100
XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, // type
101
p_next_pointer, // next
102
false, // supportsHandTracking
103
};
104
105
return &handTrackingSystemProperties;
106
}
107
108
void OpenXRHandTrackingExtension::on_state_ready() {
109
if (!handTrackingSystemProperties.supportsHandTracking) {
110
// not supported...
111
return;
112
}
113
114
// Setup our hands and reset data
115
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
116
// we'll do this later
117
hand_trackers[i].is_initialized = false;
118
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
119
120
hand_trackers[i].locations.isActive = false;
121
122
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; j++) {
123
hand_trackers[i].joint_locations[j] = { 0, { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }, 0.0 };
124
hand_trackers[i].joint_velocities[j] = { 0, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } };
125
}
126
}
127
}
128
129
void OpenXRHandTrackingExtension::on_process() {
130
if (!handTrackingSystemProperties.supportsHandTracking) {
131
// not supported...
132
return;
133
}
134
135
// process our hands
136
const XrTime time = OpenXRAPI::get_singleton()->get_predicted_display_time();
137
if (time == 0) {
138
// we don't have timing info yet, or we're skipping a frame...
139
return;
140
}
141
142
XrResult result;
143
144
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
145
if (hand_trackers[i].hand_tracker == XR_NULL_HANDLE) {
146
void *next_pointer = nullptr;
147
148
// Originally not all XR runtimes supported hand tracking data sourced both from controllers and normal hand tracking.
149
// With this extension we can indicate we wish to accept input from either or both sources.
150
// This functionality is subject to the abilities of the XR runtime and requires the data source extension.
151
// Note: If the data source extension is not available, no guarantees can be made on what the XR runtime supports.
152
uint32_t data_source_count = 0;
153
XrHandTrackingDataSourceEXT data_sources[2];
154
if (unobstructed_data_source) {
155
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT;
156
}
157
if (controller_data_source) {
158
data_sources[data_source_count++] = XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT;
159
}
160
XrHandTrackingDataSourceInfoEXT data_source_info = { XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT, next_pointer, data_source_count, data_sources };
161
if (hand_tracking_source_ext) {
162
// If supported include this info
163
next_pointer = &data_source_info;
164
}
165
166
XrHandTrackerCreateInfoEXT create_info = {
167
XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, // type
168
next_pointer, // next
169
i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, // hand
170
XR_HAND_JOINT_SET_DEFAULT_EXT, // handJointSet
171
};
172
173
result = xrCreateHandTrackerEXT(OpenXRAPI::get_singleton()->get_session(), &create_info, &hand_trackers[i].hand_tracker);
174
if (XR_FAILED(result)) {
175
// not successful? then we do nothing.
176
print_line("OpenXR: Failed to obtain hand tracking information [", OpenXRAPI::get_singleton()->get_error_string(result), "]");
177
hand_trackers[i].is_initialized = false;
178
} else {
179
next_pointer = nullptr;
180
181
hand_trackers[i].velocities.type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
182
hand_trackers[i].velocities.next = next_pointer;
183
hand_trackers[i].velocities.jointCount = XR_HAND_JOINT_COUNT_EXT;
184
hand_trackers[i].velocities.jointVelocities = hand_trackers[i].joint_velocities;
185
next_pointer = &hand_trackers[i].velocities;
186
187
if (hand_tracking_source_ext) {
188
hand_trackers[i].data_source.type = XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT;
189
hand_trackers[i].data_source.next = next_pointer;
190
hand_trackers[i].data_source.isActive = false;
191
hand_trackers[i].data_source.dataSource = XrHandTrackingDataSourceEXT(0);
192
next_pointer = &hand_trackers[i].data_source;
193
}
194
195
// Needed for vendor hand tracking extensions implemented from GDExtension.
196
for (OpenXRExtensionWrapper *wrapper : OpenXRAPI::get_singleton()->get_registered_extension_wrappers()) {
197
void *np = wrapper->set_hand_joint_locations_and_get_next_pointer(i, next_pointer);
198
if (np != nullptr) {
199
next_pointer = np;
200
}
201
}
202
203
hand_trackers[i].locations.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
204
hand_trackers[i].locations.next = next_pointer;
205
hand_trackers[i].locations.isActive = false;
206
hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT;
207
hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations;
208
209
Ref<XRHandTracker> godot_tracker;
210
godot_tracker.instantiate();
211
godot_tracker->set_tracker_hand(i == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
212
godot_tracker->set_tracker_name(i == 0 ? "/user/hand_tracker/left" : "/user/hand_tracker/right");
213
XRServer::get_singleton()->add_tracker(godot_tracker);
214
hand_trackers[i].godot_tracker = godot_tracker;
215
216
hand_trackers[i].is_initialized = true;
217
}
218
}
219
220
if (hand_trackers[i].is_initialized) {
221
Ref<XRHandTracker> godot_tracker = hand_trackers[i].godot_tracker;
222
void *next_pointer = nullptr;
223
224
XrHandJointsMotionRangeInfoEXT motion_range_info = { XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, next_pointer, hand_trackers[i].motion_range };
225
if (hand_motion_range_ext) {
226
next_pointer = &motion_range_info;
227
}
228
229
XrHandJointsLocateInfoEXT locateInfo = {
230
XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, // type
231
next_pointer, // next
232
OpenXRAPI::get_singleton()->get_play_space(), // baseSpace
233
time, // time
234
};
235
236
result = xrLocateHandJointsEXT(hand_trackers[i].hand_tracker, &locateInfo, &hand_trackers[i].locations);
237
if (XR_FAILED(result)) {
238
// not successful? then we do nothing.
239
print_line("OpenXR: Failed to get tracking for hand", i, "[", OpenXRAPI::get_singleton()->get_error_string(result), "]");
240
godot_tracker->set_hand_tracking_source(XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
241
godot_tracker->set_has_tracking_data(false);
242
godot_tracker->invalidate_pose("default");
243
continue;
244
}
245
246
// For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large
247
const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose;
248
if (!hand_trackers[i].locations.isActive || std::isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) {
249
hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive
250
}
251
252
if (hand_trackers[i].locations.isActive) {
253
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
254
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
255
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
256
const Quaternion bone_adjustment(0.0, -Math::SQRT12, Math::SQRT12, 0.0);
257
258
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
259
const XrHandJointLocationEXT &location = hand_trackers[i].joint_locations[joint];
260
const XrHandJointVelocityEXT &velocity = hand_trackers[i].joint_velocities[joint];
261
const XrPosef &pose = location.pose;
262
263
Transform3D transform;
264
Vector3 linear_velocity;
265
Vector3 angular_velocity;
266
BitField<XRHandTracker::HandJointFlags> flags = {};
267
268
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
269
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
270
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_VALID);
271
transform.basis = Basis(Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * bone_adjustment);
272
}
273
}
274
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
275
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_VALID);
276
transform.origin = Vector3(pose.position.x, pose.position.y, pose.position.z);
277
}
278
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) {
279
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ORIENTATION_TRACKED);
280
}
281
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) {
282
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_POSITION_TRACKED);
283
}
284
if (location.locationFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
285
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_LINEAR_VELOCITY_VALID);
286
linear_velocity = Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
287
godot_tracker->set_hand_joint_linear_velocity((XRHandTracker::HandJoint)joint, linear_velocity);
288
}
289
if (location.locationFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) {
290
flags.set_flag(XRHandTracker::HAND_JOINT_FLAG_ANGULAR_VELOCITY_VALID);
291
angular_velocity = Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
292
godot_tracker->set_hand_joint_angular_velocity((XRHandTracker::HandJoint)joint, angular_velocity);
293
}
294
295
godot_tracker->set_hand_joint_flags((XRHandTracker::HandJoint)joint, flags);
296
godot_tracker->set_hand_joint_transform((XRHandTracker::HandJoint)joint, transform);
297
godot_tracker->set_hand_joint_radius((XRHandTracker::HandJoint)joint, location.radius);
298
299
if (joint == XR_HAND_JOINT_PALM_EXT) {
300
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
301
XrHandTrackingDataSourceStateEXT &data_source = hand_trackers[i].data_source;
302
303
XRHandTracker::HandTrackingSource source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
304
if (hand_tracking_source_ext) {
305
if (!data_source.isActive) {
306
source = XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED;
307
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
308
source = XRHandTracker::HAND_TRACKING_SOURCE_UNOBSTRUCTED;
309
} else if (data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
310
source = XRHandTracker::HAND_TRACKING_SOURCE_CONTROLLER;
311
} else {
312
// Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
313
WARN_PRINT_ONCE("Unknown active data source found!");
314
source = XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN;
315
}
316
}
317
godot_tracker->set_hand_tracking_source(source);
318
godot_tracker->set_has_tracking_data(true);
319
godot_tracker->set_pose("default", transform, linear_velocity, angular_velocity);
320
} else {
321
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
322
godot_tracker->set_has_tracking_data(false);
323
godot_tracker->invalidate_pose("default");
324
}
325
}
326
}
327
} else {
328
godot_tracker->set_hand_tracking_source(hand_tracking_source_ext ? XRHandTracker::HAND_TRACKING_SOURCE_NOT_TRACKED : XRHandTracker::HAND_TRACKING_SOURCE_UNKNOWN);
329
godot_tracker->set_has_tracking_data(false);
330
godot_tracker->invalidate_pose("default");
331
}
332
}
333
}
334
}
335
336
void OpenXRHandTrackingExtension::on_state_stopping() {
337
// cleanup
338
cleanup_hand_tracking();
339
}
340
341
void OpenXRHandTrackingExtension::cleanup_hand_tracking() {
342
XRServer *xr_server = XRServer::get_singleton();
343
ERR_FAIL_NULL(xr_server);
344
345
for (int i = 0; i < OPENXR_MAX_TRACKED_HANDS; i++) {
346
if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) {
347
xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker);
348
349
hand_trackers[i].is_initialized = false;
350
hand_trackers[i].hand_tracker = XR_NULL_HANDLE;
351
352
XRServer::get_singleton()->remove_tracker(hand_trackers[i].godot_tracker);
353
}
354
}
355
}
356
357
bool OpenXRHandTrackingExtension::get_active() {
358
return handTrackingSystemProperties.supportsHandTracking;
359
}
360
361
const OpenXRHandTrackingExtension::HandTracker *OpenXRHandTrackingExtension::get_hand_tracker(HandTrackedHands p_hand) const {
362
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, nullptr);
363
364
return &hand_trackers[p_hand];
365
}
366
367
XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(HandTrackedHands p_hand) const {
368
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT);
369
370
return hand_trackers[p_hand].motion_range;
371
}
372
373
OpenXRHandTrackingExtension::HandTrackedSource OpenXRHandTrackingExtension::get_hand_tracking_source(HandTrackedHands p_hand) const {
374
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, OPENXR_SOURCE_UNKNOWN);
375
376
if (hand_tracking_source_ext) {
377
if (!hand_trackers[p_hand].data_source.isActive) {
378
return OPENXR_SOURCE_NOT_TRACKED;
379
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT) {
380
return OPENXR_SOURCE_UNOBSTRUCTED;
381
} else if (hand_trackers[p_hand].data_source.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) {
382
return OPENXR_SOURCE_CONTROLLER;
383
} else {
384
// Data source shouldn't be active, if new data sources are added to OpenXR we need to enable them.
385
WARN_PRINT_ONCE("Unknown active data source found!");
386
return OPENXR_SOURCE_UNKNOWN;
387
}
388
}
389
390
return OPENXR_SOURCE_UNKNOWN;
391
}
392
393
void OpenXRHandTrackingExtension::set_motion_range(HandTrackedHands p_hand, XrHandJointsMotionRangeEXT p_motion_range) {
394
ERR_FAIL_UNSIGNED_INDEX(p_hand, OPENXR_MAX_TRACKED_HANDS);
395
hand_trackers[p_hand].motion_range = p_motion_range;
396
}
397
398
XrSpaceLocationFlags OpenXRHandTrackingExtension::get_hand_joint_location_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
399
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceLocationFlags(0));
400
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceLocationFlags(0));
401
402
if (!hand_trackers[p_hand].is_initialized) {
403
return XrSpaceLocationFlags(0);
404
}
405
406
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
407
return location.locationFlags;
408
}
409
410
Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
411
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Quaternion());
412
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion());
413
414
if (!hand_trackers[p_hand].is_initialized) {
415
return Quaternion();
416
}
417
418
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
419
return Quaternion(location.pose.orientation.x, location.pose.orientation.y, location.pose.orientation.z, location.pose.orientation.w);
420
}
421
422
Vector3 OpenXRHandTrackingExtension::get_hand_joint_position(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
423
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
424
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
425
426
if (!hand_trackers[p_hand].is_initialized) {
427
return Vector3();
428
}
429
430
const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint];
431
return Vector3(location.pose.position.x, location.pose.position.y, location.pose.position.z);
432
}
433
434
float OpenXRHandTrackingExtension::get_hand_joint_radius(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
435
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, 0.0);
436
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, 0.0);
437
438
if (!hand_trackers[p_hand].is_initialized) {
439
return 0.0;
440
}
441
442
return hand_trackers[p_hand].joint_locations[p_joint].radius;
443
}
444
445
XrSpaceVelocityFlags OpenXRHandTrackingExtension::get_hand_joint_velocity_flags(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
446
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, XrSpaceVelocityFlags(0));
447
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, XrSpaceVelocityFlags(0));
448
449
if (!hand_trackers[p_hand].is_initialized) {
450
return XrSpaceVelocityFlags(0);
451
}
452
453
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
454
return velocity.velocityFlags;
455
}
456
457
Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
458
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
459
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
460
461
if (!hand_trackers[p_hand].is_initialized) {
462
return Vector3();
463
}
464
465
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
466
return Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z);
467
}
468
469
Vector3 OpenXRHandTrackingExtension::get_hand_joint_angular_velocity(HandTrackedHands p_hand, XrHandJointEXT p_joint) const {
470
ERR_FAIL_UNSIGNED_INDEX_V(p_hand, OPENXR_MAX_TRACKED_HANDS, Vector3());
471
ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3());
472
473
if (!hand_trackers[p_hand].is_initialized) {
474
return Vector3();
475
}
476
477
const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint];
478
return Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z);
479
}
480
481