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/string/print_string.h" |
36 | #include "servers/xr_server.h" |
37 | |
38 | #include <openxr/openxr.h> |
39 | |
40 | OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::singleton = nullptr; |
41 | |
42 | OpenXRHandTrackingExtension *OpenXRHandTrackingExtension::get_singleton() { |
43 | return singleton; |
44 | } |
45 | |
46 | OpenXRHandTrackingExtension::OpenXRHandTrackingExtension() { |
47 | singleton = this; |
48 | |
49 | // Make sure this is cleared until we actually request it |
50 | handTrackingSystemProperties.supportsHandTracking = false; |
51 | } |
52 | |
53 | OpenXRHandTrackingExtension::~OpenXRHandTrackingExtension() { |
54 | singleton = nullptr; |
55 | } |
56 | |
57 | HashMap<String, bool *> OpenXRHandTrackingExtension::get_requested_extensions() { |
58 | HashMap<String, bool *> request_extensions; |
59 | |
60 | request_extensions[XR_EXT_HAND_TRACKING_EXTENSION_NAME] = &hand_tracking_ext; |
61 | request_extensions[XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME] = &hand_motion_range_ext; |
62 | request_extensions[XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME] = &hand_tracking_aim_state_ext; |
63 | |
64 | return request_extensions; |
65 | } |
66 | |
67 | void OpenXRHandTrackingExtension::on_instance_created(const XrInstance p_instance) { |
68 | if (hand_tracking_ext) { |
69 | EXT_INIT_XR_FUNC(xrCreateHandTrackerEXT); |
70 | EXT_INIT_XR_FUNC(xrDestroyHandTrackerEXT); |
71 | EXT_INIT_XR_FUNC(xrLocateHandJointsEXT); |
72 | |
73 | hand_tracking_ext = xrCreateHandTrackerEXT_ptr && xrDestroyHandTrackerEXT_ptr && xrLocateHandJointsEXT_ptr; |
74 | } |
75 | } |
76 | |
77 | void OpenXRHandTrackingExtension::on_session_destroyed() { |
78 | cleanup_hand_tracking(); |
79 | } |
80 | |
81 | void OpenXRHandTrackingExtension::on_instance_destroyed() { |
82 | xrCreateHandTrackerEXT_ptr = nullptr; |
83 | xrDestroyHandTrackerEXT_ptr = nullptr; |
84 | xrLocateHandJointsEXT_ptr = nullptr; |
85 | } |
86 | |
87 | void *OpenXRHandTrackingExtension::set_system_properties_and_get_next_pointer(void *p_next_pointer) { |
88 | if (!hand_tracking_ext) { |
89 | // not supported... |
90 | return p_next_pointer; |
91 | } |
92 | |
93 | handTrackingSystemProperties = { |
94 | XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, // type |
95 | p_next_pointer, // next |
96 | false, // supportsHandTracking |
97 | }; |
98 | |
99 | return &handTrackingSystemProperties; |
100 | } |
101 | |
102 | void OpenXRHandTrackingExtension::on_state_ready() { |
103 | if (!handTrackingSystemProperties.supportsHandTracking) { |
104 | // not supported... |
105 | return; |
106 | } |
107 | |
108 | // Setup our hands and reset data |
109 | for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) { |
110 | // we'll do this later |
111 | hand_trackers[i].is_initialized = false; |
112 | hand_trackers[i].hand_tracker = XR_NULL_HANDLE; |
113 | |
114 | hand_trackers[i].aimState.aimPose = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }; |
115 | hand_trackers[i].aimState.pinchStrengthIndex = 0.0; |
116 | hand_trackers[i].aimState.pinchStrengthMiddle = 0.0; |
117 | hand_trackers[i].aimState.pinchStrengthRing = 0.0; |
118 | hand_trackers[i].aimState.pinchStrengthLittle = 0.0; |
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_next_frame_time(); // This data will be used for the next frame we render |
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 < MAX_OPENXR_TRACKED_HANDS; i++) { |
145 | if (hand_trackers[i].hand_tracker == XR_NULL_HANDLE) { |
146 | XrHandTrackerCreateInfoEXT createInfo = { |
147 | XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, // type |
148 | nullptr, // next |
149 | i == 0 ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT, // hand |
150 | XR_HAND_JOINT_SET_DEFAULT_EXT, // handJointSet |
151 | }; |
152 | |
153 | result = xrCreateHandTrackerEXT(OpenXRAPI::get_singleton()->get_session(), &createInfo, &hand_trackers[i].hand_tracker); |
154 | if (XR_FAILED(result)) { |
155 | // not successful? then we do nothing. |
156 | print_line("OpenXR: Failed to obtain hand tracking information [" , OpenXRAPI::get_singleton()->get_error_string(result), "]" ); |
157 | hand_trackers[i].is_initialized = false; |
158 | } else { |
159 | void *next_pointer = nullptr; |
160 | if (hand_tracking_aim_state_ext) { |
161 | hand_trackers[i].aimState.type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB; |
162 | hand_trackers[i].aimState.next = next_pointer; |
163 | hand_trackers[i].aimState.status = 0; |
164 | hand_trackers[i].aimState.aimPose = { { 0.0, 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }; |
165 | hand_trackers[i].aimState.pinchStrengthIndex = 0.0; |
166 | hand_trackers[i].aimState.pinchStrengthMiddle = 0.0; |
167 | hand_trackers[i].aimState.pinchStrengthRing = 0.0; |
168 | hand_trackers[i].aimState.pinchStrengthLittle = 0.0; |
169 | |
170 | next_pointer = &hand_trackers[i].aimState; |
171 | } |
172 | |
173 | hand_trackers[i].velocities.type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT; |
174 | hand_trackers[i].velocities.next = next_pointer; |
175 | hand_trackers[i].velocities.jointCount = XR_HAND_JOINT_COUNT_EXT; |
176 | hand_trackers[i].velocities.jointVelocities = hand_trackers[i].joint_velocities; |
177 | next_pointer = &hand_trackers[i].velocities; |
178 | |
179 | hand_trackers[i].locations.type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT; |
180 | hand_trackers[i].locations.next = next_pointer; |
181 | hand_trackers[i].locations.isActive = false; |
182 | hand_trackers[i].locations.jointCount = XR_HAND_JOINT_COUNT_EXT; |
183 | hand_trackers[i].locations.jointLocations = hand_trackers[i].joint_locations; |
184 | |
185 | hand_trackers[i].is_initialized = true; |
186 | } |
187 | } |
188 | |
189 | if (hand_trackers[i].is_initialized) { |
190 | void *next_pointer = nullptr; |
191 | |
192 | XrHandJointsMotionRangeInfoEXT motionRangeInfo; |
193 | |
194 | if (hand_motion_range_ext) { |
195 | motionRangeInfo.type = XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT; |
196 | motionRangeInfo.next = next_pointer; |
197 | motionRangeInfo.handJointsMotionRange = hand_trackers[i].motion_range; |
198 | |
199 | next_pointer = &motionRangeInfo; |
200 | } |
201 | |
202 | XrHandJointsLocateInfoEXT locateInfo = { |
203 | XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, // type |
204 | next_pointer, // next |
205 | OpenXRAPI::get_singleton()->get_play_space(), // baseSpace |
206 | time, // time |
207 | }; |
208 | |
209 | result = xrLocateHandJointsEXT(hand_trackers[i].hand_tracker, &locateInfo, &hand_trackers[i].locations); |
210 | if (XR_FAILED(result)) { |
211 | // not successful? then we do nothing. |
212 | print_line("OpenXR: Failed to get tracking for hand" , i, "[" , OpenXRAPI::get_singleton()->get_error_string(result), "]" ); |
213 | continue; |
214 | } |
215 | |
216 | // For some reason an inactive controller isn't coming back as inactive but has coordinates either as NAN or very large |
217 | const XrPosef &palm = hand_trackers[i].joint_locations[XR_HAND_JOINT_PALM_EXT].pose; |
218 | if ( |
219 | !hand_trackers[i].locations.isActive || isnan(palm.position.x) || palm.position.x < -1000000.00 || palm.position.x > 1000000.00) { |
220 | hand_trackers[i].locations.isActive = false; // workaround, make sure its inactive |
221 | } |
222 | |
223 | /* TODO change this to managing the controller from openxr_interface |
224 | if (hand_tracking_aim_state_ext && hand_trackers[i].locations.isActive && check_bit(XR_HAND_TRACKING_AIM_VALID_BIT_FB, hand_trackers[i].aimState.status)) { |
225 | // Controllers are updated based on the aim state's pose and pinches' strength |
226 | if (hand_trackers[i].aim_state_godot_controller == -1) { |
227 | hand_trackers[i].aim_state_godot_controller = |
228 | arvr_api->godot_arvr_add_controller( |
229 | const_cast<char *>(hand_controller_names[i]), |
230 | i + HAND_CONTROLLER_ID_OFFSET, |
231 | true, |
232 | true); |
233 | } |
234 | } |
235 | */ |
236 | } |
237 | } |
238 | } |
239 | |
240 | void OpenXRHandTrackingExtension::on_state_stopping() { |
241 | // cleanup |
242 | cleanup_hand_tracking(); |
243 | } |
244 | |
245 | void OpenXRHandTrackingExtension::cleanup_hand_tracking() { |
246 | XRServer *xr_server = XRServer::get_singleton(); |
247 | ERR_FAIL_NULL(xr_server); |
248 | |
249 | for (int i = 0; i < MAX_OPENXR_TRACKED_HANDS; i++) { |
250 | if (hand_trackers[i].hand_tracker != XR_NULL_HANDLE) { |
251 | xrDestroyHandTrackerEXT(hand_trackers[i].hand_tracker); |
252 | |
253 | hand_trackers[i].is_initialized = false; |
254 | hand_trackers[i].hand_tracker = XR_NULL_HANDLE; |
255 | } |
256 | } |
257 | } |
258 | |
259 | bool OpenXRHandTrackingExtension::get_active() { |
260 | return handTrackingSystemProperties.supportsHandTracking; |
261 | } |
262 | |
263 | const OpenXRHandTrackingExtension::HandTracker *OpenXRHandTrackingExtension::get_hand_tracker(uint32_t p_hand) const { |
264 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, nullptr); |
265 | |
266 | return &hand_trackers[p_hand]; |
267 | } |
268 | |
269 | XrHandJointsMotionRangeEXT OpenXRHandTrackingExtension::get_motion_range(uint32_t p_hand) const { |
270 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, XR_HAND_JOINTS_MOTION_RANGE_MAX_ENUM_EXT); |
271 | |
272 | return hand_trackers[p_hand].motion_range; |
273 | } |
274 | |
275 | void OpenXRHandTrackingExtension::set_motion_range(uint32_t p_hand, XrHandJointsMotionRangeEXT p_motion_range) { |
276 | ERR_FAIL_UNSIGNED_INDEX(p_hand, MAX_OPENXR_TRACKED_HANDS); |
277 | hand_trackers[p_hand].motion_range = p_motion_range; |
278 | } |
279 | |
280 | Quaternion OpenXRHandTrackingExtension::get_hand_joint_rotation(uint32_t p_hand, XrHandJointEXT p_joint) const { |
281 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Quaternion()); |
282 | ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Quaternion()); |
283 | |
284 | if (!hand_trackers[p_hand].is_initialized) { |
285 | return Quaternion(); |
286 | } |
287 | |
288 | const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint]; |
289 | return Quaternion(location.pose.orientation.x, location.pose.orientation.y, location.pose.orientation.z, location.pose.orientation.w); |
290 | } |
291 | |
292 | Vector3 OpenXRHandTrackingExtension::get_hand_joint_position(uint32_t p_hand, XrHandJointEXT p_joint) const { |
293 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3()); |
294 | ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3()); |
295 | |
296 | if (!hand_trackers[p_hand].is_initialized) { |
297 | return Vector3(); |
298 | } |
299 | |
300 | const XrHandJointLocationEXT &location = hand_trackers[p_hand].joint_locations[p_joint]; |
301 | return Vector3(location.pose.position.x, location.pose.position.y, location.pose.position.z); |
302 | } |
303 | |
304 | float OpenXRHandTrackingExtension::get_hand_joint_radius(uint32_t p_hand, XrHandJointEXT p_joint) const { |
305 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, 0.0); |
306 | ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, 0.0); |
307 | |
308 | if (!hand_trackers[p_hand].is_initialized) { |
309 | return 0.0; |
310 | } |
311 | |
312 | return hand_trackers[p_hand].joint_locations[p_joint].radius; |
313 | } |
314 | |
315 | Vector3 OpenXRHandTrackingExtension::get_hand_joint_linear_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const { |
316 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3()); |
317 | ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3()); |
318 | |
319 | if (!hand_trackers[p_hand].is_initialized) { |
320 | return Vector3(); |
321 | } |
322 | |
323 | const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint]; |
324 | return Vector3(velocity.linearVelocity.x, velocity.linearVelocity.y, velocity.linearVelocity.z); |
325 | } |
326 | |
327 | Vector3 OpenXRHandTrackingExtension::get_hand_joint_angular_velocity(uint32_t p_hand, XrHandJointEXT p_joint) const { |
328 | ERR_FAIL_UNSIGNED_INDEX_V(p_hand, MAX_OPENXR_TRACKED_HANDS, Vector3()); |
329 | ERR_FAIL_UNSIGNED_INDEX_V(p_joint, XR_HAND_JOINT_COUNT_EXT, Vector3()); |
330 | |
331 | if (!hand_trackers[p_hand].is_initialized) { |
332 | return Vector3(); |
333 | } |
334 | |
335 | const XrHandJointVelocityEXT &velocity = hand_trackers[p_hand].joint_velocities[p_joint]; |
336 | return Vector3(velocity.angularVelocity.x, velocity.angularVelocity.y, velocity.angularVelocity.z); |
337 | } |
338 | |