Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/openxr/scene/openxr_hand.cpp
10278 views
1
/**************************************************************************/
2
/* openxr_hand.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.h"
32
33
#include "../extensions/openxr_hand_tracking_extension.h"
34
#include "../openxr_api.h"
35
36
#include "scene/3d/skeleton_3d.h"
37
#include "servers/xr_server.h"
38
39
void OpenXRHand::_bind_methods() {
40
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
41
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
42
43
ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
44
ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
45
46
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
47
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
48
49
ClassDB::bind_method(D_METHOD("set_skeleton_rig", "skeleton_rig"), &OpenXRHand::set_skeleton_rig);
50
ClassDB::bind_method(D_METHOD("get_skeleton_rig"), &OpenXRHand::get_skeleton_rig);
51
52
ClassDB::bind_method(D_METHOD("set_bone_update", "bone_update"), &OpenXRHand::set_bone_update);
53
ClassDB::bind_method(D_METHOD("get_bone_update"), &OpenXRHand::get_bone_update);
54
55
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
56
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
57
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
58
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
59
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
60
61
BIND_ENUM_CONSTANT(HAND_LEFT);
62
BIND_ENUM_CONSTANT(HAND_RIGHT);
63
BIND_ENUM_CONSTANT(HAND_MAX);
64
65
BIND_ENUM_CONSTANT(MOTION_RANGE_UNOBSTRUCTED);
66
BIND_ENUM_CONSTANT(MOTION_RANGE_CONFORM_TO_CONTROLLER);
67
BIND_ENUM_CONSTANT(MOTION_RANGE_MAX);
68
69
BIND_ENUM_CONSTANT(SKELETON_RIG_OPENXR);
70
BIND_ENUM_CONSTANT(SKELETON_RIG_HUMANOID);
71
BIND_ENUM_CONSTANT(SKELETON_RIG_MAX);
72
73
BIND_ENUM_CONSTANT(BONE_UPDATE_FULL);
74
BIND_ENUM_CONSTANT(BONE_UPDATE_ROTATION_ONLY);
75
BIND_ENUM_CONSTANT(BONE_UPDATE_MAX);
76
}
77
78
OpenXRHand::OpenXRHand() {
79
openxr_api = OpenXRAPI::get_singleton();
80
hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton();
81
}
82
83
void OpenXRHand::set_hand(Hands p_hand) {
84
ERR_FAIL_INDEX(p_hand, HAND_MAX);
85
86
hand = p_hand;
87
}
88
89
OpenXRHand::Hands OpenXRHand::get_hand() const {
90
return hand;
91
}
92
93
void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
94
hand_skeleton = p_hand_skeleton;
95
96
// TODO if inside tree call _get_bones()
97
}
98
99
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
100
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
101
motion_range = p_motion_range;
102
103
_set_motion_range();
104
}
105
106
OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
107
return motion_range;
108
}
109
110
NodePath OpenXRHand::get_hand_skeleton() const {
111
return hand_skeleton;
112
}
113
114
void OpenXRHand::_set_motion_range() {
115
if (!hand_tracking_ext) {
116
return;
117
}
118
119
XrHandJointsMotionRangeEXT xr_motion_range;
120
switch (motion_range) {
121
case MOTION_RANGE_UNOBSTRUCTED:
122
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT;
123
break;
124
case MOTION_RANGE_CONFORM_TO_CONTROLLER:
125
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
126
break;
127
default:
128
xr_motion_range = XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT;
129
break;
130
}
131
132
hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(hand), xr_motion_range);
133
}
134
135
void OpenXRHand::set_skeleton_rig(SkeletonRig p_skeleton_rig) {
136
ERR_FAIL_INDEX(p_skeleton_rig, SKELETON_RIG_MAX);
137
138
skeleton_rig = p_skeleton_rig;
139
}
140
141
OpenXRHand::SkeletonRig OpenXRHand::get_skeleton_rig() const {
142
return skeleton_rig;
143
}
144
145
void OpenXRHand::set_bone_update(BoneUpdate p_bone_update) {
146
ERR_FAIL_INDEX(p_bone_update, BONE_UPDATE_MAX);
147
148
bone_update = p_bone_update;
149
}
150
151
OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
152
return bone_update;
153
}
154
155
Skeleton3D *OpenXRHand::get_skeleton() {
156
if (!has_node(hand_skeleton)) {
157
return nullptr;
158
}
159
160
Node *node = get_node(hand_skeleton);
161
if (!node) {
162
return nullptr;
163
}
164
165
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
166
return skeleton;
167
}
168
169
void OpenXRHand::_get_joint_data() {
170
// Table of bone names for different rig types.
171
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
172
// SKELETON_RIG_OPENXR bone names.
173
{
174
"Palm",
175
"Wrist",
176
"Thumb_Metacarpal",
177
"Thumb_Proximal",
178
"Thumb_Distal",
179
"Thumb_Tip",
180
"Index_Metacarpal",
181
"Index_Proximal",
182
"Index_Intermediate",
183
"Index_Distal",
184
"Index_Tip",
185
"Middle_Metacarpal",
186
"Middle_Proximal",
187
"Middle_Intermediate",
188
"Middle_Distal",
189
"Middle_Tip",
190
"Ring_Metacarpal",
191
"Ring_Proximal",
192
"Ring_Intermediate",
193
"Ring_Distal",
194
"Ring_Tip",
195
"Little_Metacarpal",
196
"Little_Proximal",
197
"Little_Intermediate",
198
"Little_Distal",
199
"Little_Tip" },
200
201
// SKELETON_RIG_HUMANOID bone names.
202
{
203
"Palm",
204
"Hand",
205
"ThumbMetacarpal",
206
"ThumbProximal",
207
"ThumbDistal",
208
"ThumbTip",
209
"IndexMetacarpal",
210
"IndexProximal",
211
"IndexIntermediate",
212
"IndexDistal",
213
"IndexTip",
214
"MiddleMetacarpal",
215
"MiddleProximal",
216
"MiddleIntermediate",
217
"MiddleDistal",
218
"MiddleTip",
219
"RingMetacarpal",
220
"RingProximal",
221
"RingIntermediate",
222
"RingDistal",
223
"RingTip",
224
"LittleMetacarpal",
225
"LittleProximal",
226
"LittleIntermediate",
227
"LittleDistal",
228
"LittleTip" }
229
};
230
231
// Table of bone name formats for different rig types and left/right hands.
232
static const String bone_name_formats[SKELETON_RIG_MAX][2] = {
233
// SKELETON_RIG_OPENXR bone name format.
234
{ "<bone>_L", "<bone>_R" },
235
236
// SKELETON_RIG_HUMANOID bone name format.
237
{ "Left<bone>", "Right<bone>" }
238
};
239
240
// reset JIC
241
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
242
joints[i].bone = -1;
243
joints[i].parent_joint = -1;
244
}
245
246
Skeleton3D *skeleton = get_skeleton();
247
if (!skeleton) {
248
return;
249
}
250
251
// Find the skeleton-bones associated with each OpenXR joint.
252
int bones[XR_HAND_JOINT_COUNT_EXT];
253
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
254
// Construct the expected bone name.
255
String bone_name = bone_name_formats[skeleton_rig][hand].replace("<bone>", bone_names[skeleton_rig][i]);
256
257
// Find the skeleton bone.
258
bones[i] = skeleton->find_bone(bone_name);
259
if (bones[i] == -1) {
260
print_line("Couldn't obtain bone for", bone_name);
261
}
262
}
263
264
// Assemble the OpenXR joint relationship to the available skeleton bones.
265
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
266
// Get the skeleton bone (skip if not found).
267
const int bone = bones[i];
268
if (bone == -1) {
269
continue;
270
}
271
272
// Find the parent skeleton-bone.
273
const int parent_bone = skeleton->get_bone_parent(bone);
274
if (parent_bone == -1) {
275
// If no parent skeleton-bone exists then drive this relative to palm joint.
276
joints[i].bone = bone;
277
joints[i].parent_joint = XR_HAND_JOINT_PALM_EXT;
278
continue;
279
}
280
281
// Find the OpenXR joint associated with the parent skeleton-bone.
282
for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j) {
283
if (bones[j] == parent_bone) {
284
// If a parent joint is found then drive this bone relative to it.
285
joints[i].bone = bone;
286
joints[i].parent_joint = j;
287
break;
288
}
289
}
290
}
291
}
292
293
void OpenXRHand::_update_skeleton() {
294
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
295
return;
296
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
297
return;
298
}
299
300
Skeleton3D *skeleton = get_skeleton();
301
if (!skeleton) {
302
return;
303
}
304
305
// Table of bone adjustments for different rig types
306
static const Quaternion bone_adjustments[SKELETON_RIG_MAX] = {
307
// SKELETON_RIG_OPENXR bone adjustment. This is an identity quaternion
308
// because the incoming quaternions are already in OpenXR format.
309
Quaternion(),
310
311
// SKELETON_RIG_HUMANOID bone adjustment. This rotation performs:
312
// OpenXR Z+ -> Godot Humanoid Y- (Back along the bone)
313
// OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand)
314
Quaternion(0.0, -Math::SQRT12, Math::SQRT12, 0.0),
315
};
316
317
// we cache our transforms so we can quickly calculate local transforms
318
XRPose::TrackingConfidence confidences[XR_HAND_JOINT_COUNT_EXT];
319
Quaternion quaternions[XR_HAND_JOINT_COUNT_EXT];
320
Quaternion inv_quaternions[XR_HAND_JOINT_COUNT_EXT];
321
Vector3 positions[XR_HAND_JOINT_COUNT_EXT];
322
323
const Quaternion &rig_adjustment = bone_adjustments[skeleton_rig];
324
const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(OpenXRHandTrackingExtension::HandTrackedHands(hand));
325
const float ws = XRServer::get_singleton()->get_world_scale();
326
327
if (hand_tracker->is_initialized && hand_tracker->locations.isActive) {
328
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
329
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_NONE;
330
quaternions[i] = Quaternion();
331
positions[i] = Vector3();
332
333
const XrHandJointLocationEXT &location = hand_tracker->joint_locations[i];
334
const XrPosef &pose = location.pose;
335
336
if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
337
if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) {
338
quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * rig_adjustment;
339
inv_quaternions[i] = quaternions[i].inverse();
340
341
if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
342
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_HIGH;
343
positions[i] = Vector3(pose.position.x * ws, pose.position.y * ws, pose.position.z * ws);
344
345
// TODO get inverse of position, we'll do this later. For now we're ignoring bone positions which generally works better anyway
346
} else {
347
confidences[i] = XRPose::XR_TRACKING_CONFIDENCE_LOW;
348
}
349
}
350
}
351
}
352
353
if (confidences[XR_HAND_JOINT_PALM_EXT] != XRPose::XR_TRACKING_CONFIDENCE_NONE) {
354
// Iterate over all the OpenXR joints.
355
for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) {
356
// Get the skeleton bone (skip if none).
357
const int bone = joints[joint].bone;
358
if (bone == -1) {
359
continue;
360
}
361
362
// Calculate the relative relationship to the parent bone joint.
363
const int parent_joint = joints[joint].parent_joint;
364
const Quaternion q = inv_quaternions[parent_joint] * quaternions[joint];
365
const Vector3 p = inv_quaternions[parent_joint].xform(positions[joint] - positions[parent_joint]);
366
367
// Update the bone position if enabled by update mode.
368
if (bone_update == BONE_UPDATE_FULL) {
369
skeleton->set_bone_pose_position(joints[joint].bone, p);
370
}
371
372
// Always update the bone rotation.
373
skeleton->set_bone_pose_rotation(joints[joint].bone, q);
374
}
375
376
// Transform the OpenXRHand to the skeleton pose.
377
Transform3D t;
378
t.basis = Basis(quaternions[XR_HAND_JOINT_PALM_EXT]);
379
t.origin = positions[XR_HAND_JOINT_PALM_EXT];
380
set_transform(t);
381
382
// show it
383
set_visible(true);
384
} else {
385
// hide it
386
set_visible(false);
387
}
388
} else {
389
// hide it
390
set_visible(false);
391
}
392
}
393
394
void OpenXRHand::_notification(int p_what) {
395
switch (p_what) {
396
case NOTIFICATION_ENTER_TREE: {
397
_get_joint_data();
398
399
set_process_internal(true);
400
} break;
401
case NOTIFICATION_EXIT_TREE: {
402
set_process_internal(false);
403
404
// reset
405
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
406
joints[i].bone = -1;
407
joints[i].parent_joint = -1;
408
}
409
} break;
410
case NOTIFICATION_INTERNAL_PROCESS: {
411
_update_skeleton();
412
} break;
413
default: {
414
} break;
415
}
416
}
417
418