Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qopenxrinputmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
9
10#include "qquick3dxrcontroller_p.h" //### InputAction enum
11
12#include <QDebug>
13
14#include <private/qquick3djoint_p.h>
15
17
19 : q_ptr(&manager)
20{
21 m_handInputState[Hand::LeftHand] = new QQuick3DXrHandInput(this);
22 m_handInputState[Hand::RightHand] = new QQuick3DXrHandInput(this);
23}
24
26{
27 teardown();
28 delete m_handInputState[Hand::LeftHand];
29 delete m_handInputState[Hand::RightHand];
30
31 m_handInputState[Hand::LeftHand] = nullptr;
32 m_handInputState[Hand::RightHand] = nullptr;
33}
34
35QQuick3DXrInputManagerPrivate::QXRHandComponentPath QQuick3DXrInputManagerPrivate::makeHandInputPaths(const QByteArrayView path)
36{
37 QXRHandComponentPath res;
38 setPath(res.paths[Hand::LeftHand], "/user/hand/left/" + path);
39 setPath(res.paths[Hand::RightHand], "/user/hand/right/" + path);
40 return res;
41}
42
43
44XrPath QQuick3DXrInputManagerPrivate::makeInputPath(const QByteArrayView path)
45{
46 XrPath res;
47 setPath(res, path.toByteArray());
48 return res;
49}
50
51QQuick3DGeometry *QQuick3DXrInputManagerPrivate::createHandMeshGeometry(const HandMeshData &handMeshData)
52{
53 QQuick3DGeometry *geometry = new QQuick3DGeometry();
55
56 // Figure out which attributes should be used
57 const qsizetype expectedLength = handMeshData.vertexPositions.size();
58 bool hasPositions = !handMeshData.vertexPositions.isEmpty();
59 bool hasNormals = handMeshData.vertexNormals.size() >= expectedLength;
60 bool hasUV0s = handMeshData.vertexUVs.size() >= expectedLength;
61 bool hasJoints = handMeshData.vertexBlendIndices.size() >= expectedLength;
62 bool hasWeights = handMeshData.vertexBlendWeights.size() >= expectedLength;
63 bool hasIndexes = !handMeshData.indices.isEmpty();
64
65 int offset = 0;
66 if (hasPositions) {
68 offset += 3 * sizeof(float);
69 }
70
71 if (hasNormals) {
73 offset += 3 * sizeof(float);
74 }
75
76 if (hasUV0s) {
78 offset += 2 * sizeof(float);
79 }
80
81 if (hasJoints) {
83 offset += 4 * sizeof(qint32);
84 }
85
86 if (hasWeights) {
88 offset += 4 * sizeof(float);
89 }
90
91 if (hasIndexes)
93
94 // set up the vertex buffer
95 const int stride = offset;
96 const qsizetype bufferSize = expectedLength * stride;
97 geometry->setStride(stride);
98
99 QByteArray vertexBuffer;
100 vertexBuffer.reserve(bufferSize);
101
102 QVector3D minBounds;
103 QVector3D maxBounds;
104
105 auto appendFloat = [&vertexBuffer](float f) {
106 vertexBuffer.append(reinterpret_cast<const char *>(&f), sizeof(float));
107 };
108 auto appendInt = [&vertexBuffer](qint32 i) {
109 vertexBuffer.append(reinterpret_cast<const char *>(&i), sizeof(qint32));
110 };
111
112 for (qsizetype i = 0; i < expectedLength; ++i) {
113 // start writing float values to vertexBuffer
114 if (hasPositions) {
115 const QVector3D position = OpenXRHelpers::toQVector(handMeshData.vertexPositions[i]);
116 appendFloat(position.x());
117 appendFloat(position.y());
118 appendFloat(position.z());
119 minBounds.setX(qMin(minBounds.x(), position.x()));
120 maxBounds.setX(qMax(maxBounds.x(), position.x()));
121 minBounds.setY(qMin(minBounds.y(), position.y()));
122 maxBounds.setY(qMax(maxBounds.y(), position.y()));
123 minBounds.setZ(qMin(minBounds.z(), position.z()));
124 maxBounds.setZ(qMax(maxBounds.z(), position.z()));
125 }
126 if (hasNormals) {
127 const auto &normal = handMeshData.vertexNormals[i];
128 appendFloat(normal.x);
129 appendFloat(normal.y);
130 appendFloat(normal.z);
131 }
132
133 if (hasUV0s) {
134 const auto &uv0 = handMeshData.vertexUVs[i];
135 appendFloat(uv0.x);
136 appendFloat(uv0.y);
137 }
138
139 if (hasJoints) {
140 const auto &joint = handMeshData.vertexBlendIndices[i];
141 appendInt(joint.x);
142 appendInt(joint.y);
143 appendInt(joint.z);
144 appendInt(joint.w);
145 }
146
147 if (hasWeights) {
148 const auto &weight = handMeshData.vertexBlendWeights[i];
149 appendFloat(weight.x);
150 appendFloat(weight.y);
151 appendFloat(weight.z);
152 appendFloat(weight.w);
153 }
154 }
155
156 geometry->setBounds(minBounds, maxBounds);
157 geometry->setVertexData(vertexBuffer);
158
159 // Index Buffer
160 if (hasIndexes) {
161 const qsizetype indexLength = handMeshData.indices.size();
162 QByteArray indexBuffer;
163 indexBuffer.reserve(indexLength * sizeof(int16_t));
164 for (qsizetype i = 0; i < indexLength; ++i) {
165 const auto &index = handMeshData.indices[i];
166 indexBuffer.append(reinterpret_cast<const char *>(&index), sizeof(int16_t));
167 }
168 geometry->setIndexData(indexBuffer);
169 }
170
171 return geometry;
172}
173
174void QQuick3DXrInputManagerPrivate::init(XrInstance instance, XrSession session)
175{
176 if (m_initialized) {
177 qWarning() << "QQuick3DXrInputManager: Trying to initialize an already initialized session";
178 teardown();
179 }
180
181 m_instance = instance;
182 m_session = session;
183
184 setupHandTracking();
185
186 setupActions();
187
188 QXRHandComponentPath aClick = makeHandInputPaths("input/a/click"); // OCULUS_TOUCH (right) | VALVE_INDEX (right + left)
189 QXRHandComponentPath bClick = makeHandInputPaths("input/b/click"); // OCULUS_TOUCH (right) | VALVE_INDEX (right + left)
190 QXRHandComponentPath aTouch = makeHandInputPaths("input/a/touch"); // OCULUS_TOUCH (right) | VALVE_INDEX (right + left)
191 QXRHandComponentPath bTouch = makeHandInputPaths("input/b/touch"); // OCULUS_TOUCH (right) | VALVE_INDEX (right + left)
192
193 QXRHandComponentPath xClick = makeHandInputPaths("input/x/click"); // OCULUS_TOUCH (left)
194 QXRHandComponentPath yClick = makeHandInputPaths("input/y/click"); // OCULUS_TOUCH (left)
195 QXRHandComponentPath xTouch = makeHandInputPaths("input/x/touch"); // OCULUS_TOUCH (left)
196 QXRHandComponentPath yTouch = makeHandInputPaths("input/y/touch"); // OCULUS_TOUCH (left)
197
198 QXRHandComponentPath menuClick = makeHandInputPaths("input/menu/click"); // OCULUS_TOUCH (left) | MICROSOFT_MRM (right + left) | HTC_VIVE (right + left)
199 QXRHandComponentPath systemClick = makeHandInputPaths("input/system/click"); // OCULUS_TOUCH (right) | VALVE_INDEX (right + left) | HTC_VIVE (right + left)
200 QXRHandComponentPath systemTouch = makeHandInputPaths("input/system/touch"); // VALVE_INDEX (right + left)
201
202 QXRHandComponentPath squeezeValue = makeHandInputPaths("input/squeeze/value"); // right + left: OCULUS_TOUCH | VALVE_INDEX
203 QXRHandComponentPath squeezeForce = makeHandInputPaths("input/squeeze/force"); // right + left: VALVE_INDEX
204 QXRHandComponentPath squeezeClick = makeHandInputPaths("input/squeeze/click"); // right + left: MICROSOFT_MRM | HTC_VIVE
205
206 QXRHandComponentPath triggerValue = makeHandInputPaths("input/trigger/value"); // right + left: OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
207 QXRHandComponentPath triggerTouch = makeHandInputPaths("input/trigger/touch"); // right + left: OCULUS_TOUCH | VALVE_INDEX
208 QXRHandComponentPath triggerClick = makeHandInputPaths("input/trigger/click"); // right + left: VALVE_INDEX | HTC_VIVE
209
210 QXRHandComponentPath thumbstickX = makeHandInputPaths("input/thumbstick/x"); // OCULUS_TOUCH (right + left) | VALVE_INDEX (right + left) | MICROSOFT_MRM (left)
211 QXRHandComponentPath thumbstickY = makeHandInputPaths("input/thumbstick/y"); // OCULUS_TOUCH (right + left) | VALVE_INDEX (right + left) | MICROSOFT_MRM (left)
212 QXRHandComponentPath thumbstickClick = makeHandInputPaths("input/thumbstick/click"); // OCULUS_TOUCH (right + left) | VALVE_INDEX (right + left) | MICROSOFT_MRM (left)
213 QXRHandComponentPath thumbstickTouch = makeHandInputPaths("input/thumbstick/touch"); // OCULUS_TOUCH (right + left) | VALVE_INDEX (right + left)
214 QXRHandComponentPath thumbrestTouch = makeHandInputPaths("input/thumbrest/touch"); // OCULUS_TOUCH (right + left)
215
216 QXRHandComponentPath trackpadX = makeHandInputPaths("input/trackpad/x"); // right + left: VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
217 QXRHandComponentPath trackpadY = makeHandInputPaths("input/trackpad/y"); // right + left: VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
218 QXRHandComponentPath trackpadForce = makeHandInputPaths("input/trackpad/force"); // right + left: VALVE_INDEX
219 QXRHandComponentPath trackpadClick = makeHandInputPaths("input/trackpad/click"); // right + left: VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
220 QXRHandComponentPath trackpadTouch = makeHandInputPaths("input/trackpad/touch"); // right + left: MICROSOFT_MRM | HTC_VIVE
221
222 XrPath handLeftGripPose; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
223 XrPath handLeftAimPose; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
224 XrPath handLeftHaptic; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
225
226 XrPath handRightGripPose; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
227 XrPath handRightAimPose; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
228 XrPath handRightHaptic; // OCULUS_TOUCH | VALVE_INDEX | MICROSOFT_MRM | HTC_VIVE
229
230 // Hand Left
231
232 setPath(handLeftGripPose, "/user/hand/left/input/grip/pose");
233 setPath(handLeftAimPose, "/user/hand/left/input/aim/pose");
234 setPath(handLeftHaptic, "/user/hand/left/output/haptic");
235
236 setPath(handRightGripPose, "/user/hand/right/input/grip/pose");
237 setPath(handRightAimPose, "/user/hand/right/input/aim/pose");
238 setPath(handRightHaptic, "/user/hand/right/output/haptic");
239
240 // Bindings
241
242 using XrActionBindings = std::vector<XrActionSuggestedBinding>;
243 using HandInputMapping = std::vector<std::tuple<QQuick3DXrInputAction::Action, QXRHandComponentPath, SubPathSelector>>;
244 auto addToBindings = [this](XrActionBindings &bindings, const HandInputMapping &defs){
245 for (const auto &[actionId, path, selector] : defs) {
246 if (selector & LeftHandSubPath)
247 bindings.push_back({ m_inputActions[actionId], path.paths[Hand::LeftHand] });
248 if (selector & RightHandSubPath)
249 bindings.push_back({ m_inputActions[actionId], path.paths[Hand::RightHand] });
250 }
251 };
252
253 // Oculus Touch
254 {
255 HandInputMapping mappingDefs {
256 { QQuick3DXrInputAction::Button1Pressed, xClick, LeftHandSubPath },
257 { QQuick3DXrInputAction::Button1Pressed, aClick, RightHandSubPath },
258 { QQuick3DXrInputAction::Button2Pressed, yClick, LeftHandSubPath },
259 { QQuick3DXrInputAction::Button2Pressed, bClick, RightHandSubPath },
260 { QQuick3DXrInputAction::Button1Touched, xTouch, LeftHandSubPath },
261 { QQuick3DXrInputAction::Button1Touched, aTouch, RightHandSubPath },
262 { QQuick3DXrInputAction::Button2Touched, yTouch, LeftHandSubPath },
263 { QQuick3DXrInputAction::Button2Touched, bTouch, RightHandSubPath },
264 { QQuick3DXrInputAction::ButtonMenuPressed, menuClick, LeftHandSubPath },
265 { QQuick3DXrInputAction::ButtonSystemPressed, systemClick, RightHandSubPath },
266 { QQuick3DXrInputAction::SqueezeValue, squeezeValue, BothHandsSubPath },
267 { QQuick3DXrInputAction::TriggerValue, triggerValue, BothHandsSubPath },
268 { QQuick3DXrInputAction::TriggerTouched, triggerTouch, BothHandsSubPath },
269 { QQuick3DXrInputAction::ThumbstickX, thumbstickX, BothHandsSubPath },
270 { QQuick3DXrInputAction::ThumbstickY, thumbstickY, BothHandsSubPath },
271 { QQuick3DXrInputAction::ThumbstickPressed, thumbstickClick, BothHandsSubPath },
272 { QQuick3DXrInputAction::ThumbstickTouched, thumbstickTouch, BothHandsSubPath },
273 { QQuick3DXrInputAction::ThumbrestTouched, thumbrestTouch, BothHandsSubPath },
274 };
275
276 XrPath oculusTouchProfile;
277 setPath(oculusTouchProfile, "/interaction_profiles/oculus/touch_controller");
278 std::vector<XrActionSuggestedBinding> bindings {{
279 {m_handActions.gripPoseAction, handLeftGripPose},
280 {m_handActions.aimPoseAction, handLeftAimPose},
281 {m_handActions.hapticAction, handLeftHaptic},
282
283 {m_handActions.gripPoseAction, handRightGripPose},
284 {m_handActions.aimPoseAction, handRightAimPose},
285 {m_handActions.hapticAction, handRightHaptic},
286 }};
287
288 addToBindings(bindings, mappingDefs);
289
290 XrInteractionProfileSuggestedBinding suggestedBindings{};
291 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
292 suggestedBindings.interactionProfile = oculusTouchProfile;
293 suggestedBindings.suggestedBindings = bindings.data();
294 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
295 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
296 qWarning("Failed to get suggested interaction profile bindings for Oculus touch");
297 }
298
299 // Microsoft hand interaction extension as supported by Quest 3
300 // TODO: there are other, very similar, extensions: XR_HTC_HAND_INTERACTION_EXTENSION_NAME and XR_EXT_HAND_INTERACTION_EXTENSION_NAME
301 {
302 XrPath handInteractionProfile;
303 setPath(handInteractionProfile, "/interaction_profiles/microsoft/hand_interaction");
304 std::vector<XrActionSuggestedBinding> bindings {{
305 {m_handActions.gripPoseAction, handLeftGripPose},
306 {m_handActions.aimPoseAction, handLeftAimPose}, // ### Binding succeeds, but does not seem to work on the Quest 3
307 {m_handActions.gripPoseAction, handRightGripPose},
308 {m_handActions.aimPoseAction, handRightAimPose},
309 }};
310
311 HandInputMapping mappingDefs {
312 { QQuick3DXrInputAction::SqueezeValue, squeezeValue, BothHandsSubPath },
313 };
314
315 addToBindings(bindings, mappingDefs);
316
317 XrInteractionProfileSuggestedBinding suggestedBindings{};
318 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
319 suggestedBindings.interactionProfile = handInteractionProfile;
320 suggestedBindings.suggestedBindings = bindings.data();
321 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
322
323 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
324 qWarning("Failed to get suggested interaction profile bindings for MSFT hand interaction");
325 }
326
327 {
328 XrPath htcViveProfile;
329 setPath(htcViveProfile, "/interaction_profiles/htc/vive_controller");
330
331 HandInputMapping mappingDefs {
332 { QQuick3DXrInputAction::ButtonMenuPressed, menuClick, BothHandsSubPath },
333 { QQuick3DXrInputAction::ButtonSystemPressed, systemClick, BothHandsSubPath },
334 { QQuick3DXrInputAction::SqueezePressed, squeezeClick, BothHandsSubPath },
335 { QQuick3DXrInputAction::TriggerValue, triggerValue, BothHandsSubPath },
336 { QQuick3DXrInputAction::TriggerPressed, triggerClick, BothHandsSubPath },
337 { QQuick3DXrInputAction::TrackpadX, trackpadX, BothHandsSubPath },
338 { QQuick3DXrInputAction::TrackpadY, trackpadY, BothHandsSubPath },
339 { QQuick3DXrInputAction::TrackpadPressed, trackpadClick, BothHandsSubPath },
340 { QQuick3DXrInputAction::TrackpadTouched, trackpadTouch, BothHandsSubPath },
341 };
342
343 std::vector<XrActionSuggestedBinding> bindings {{
344 {m_handActions.gripPoseAction, handLeftGripPose},
345 {m_handActions.aimPoseAction, handLeftAimPose},
346 {m_handActions.hapticAction, handLeftHaptic},
347
348 {m_handActions.gripPoseAction, handRightGripPose},
349 {m_handActions.aimPoseAction, handRightAimPose},
350 {m_handActions.hapticAction, handRightHaptic},
351 }};
352
353 addToBindings(bindings, mappingDefs);
354
355 XrInteractionProfileSuggestedBinding suggestedBindings{};
356 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
357 suggestedBindings.interactionProfile = htcViveProfile;
358 suggestedBindings.suggestedBindings = bindings.data();
359 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
360 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
361 qWarning("Failed to get suggested interaction profile bindings for Vive controller");
362 }
363
364 // Microsoft MRM ### TODO
365 {
366 XrPath microsoftMotionProfile;
367 setPath(microsoftMotionProfile, "/interaction_profiles/microsoft/motion_controller");
368 }
369
370 // Valve Index ### TODO
371 {
372 XrPath valveIndexProfile;
373 setPath(valveIndexProfile, "/interaction_profiles/valve/index_controller");
374 }
375
376 // Setup Action Spaces
377
378 XrActionSpaceCreateInfo actionSpaceInfo{};
379 actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
380 actionSpaceInfo.action = m_handActions.gripPoseAction;
381 actionSpaceInfo.poseInActionSpace.orientation.w = 1.0f;
382 //actionSpaceInfo.poseInActionSpace.orientation.y = 1.0f;
383 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
384 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[0])))
385 qWarning("Failed to create action space for handGripSpace[0]");
386 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
387 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[1])))
388 qWarning("Failed to create action space for handGripSpace[1]");
389
390 actionSpaceInfo.action = m_handActions.aimPoseAction;
391 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
392 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[0])))
393 qWarning("Failed to create action space for handAimSpace[0]");
394 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
395 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[1])))
396 qWarning("Failed to create action space for handAimSpace[1]");
397
398 // Attach Action set to session
399
400 XrSessionActionSetsAttachInfo attachInfo{};
401 attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
402 attachInfo.countActionSets = 1;
403 attachInfo.actionSets = &m_actionSet;
404 if (!checkXrResult(xrAttachSessionActionSets(m_session, &attachInfo)))
405 qWarning("Failed to attach action sets to session");
406
407 m_initialized = true;
408}
409
411{
412 if (!m_initialized)
413 return;
414
415 m_initialized = false;
416
417 xrDestroySpace(m_handGripSpace[0]);
418 xrDestroySpace(m_handGripSpace[1]);
419 xrDestroySpace(m_handAimSpace[0]);
420 xrDestroySpace(m_handAimSpace[1]);
421
422 destroyActions();
423
425 xrDestroyHandTrackerEXT_(handTracker[Hand::LeftHand]);
426 xrDestroyHandTrackerEXT_(handTracker[Hand::RightHand]);
427 }
428
429 m_instance = {XR_NULL_HANDLE};
430 m_session = {XR_NULL_HANDLE};
431}
432
434{
435 QSSG_ASSERT(inputManager != nullptr, return nullptr);
436 return inputManager->d_func();
437}
438
440{
441 if (!m_initialized)
442 return;
443
444 // Sync Actions
445 const XrActiveActionSet activeActionSet{m_actionSet, XR_NULL_PATH};
446 XrActionsSyncInfo syncInfo{};
447 syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO;
448 syncInfo.countActiveActionSets = 1;
449 syncInfo.activeActionSets = &activeActionSet;
450 XrResult result = xrSyncActions(m_session, &syncInfo);
451 if (!(result == XR_SUCCESS ||
452 result == XR_SESSION_LOSS_PENDING ||
453 result == XR_SESSION_NOT_FOCUSED))
454 {
455 if (!checkXrResult(result)) {
456 qWarning("xrSyncActions failed");
457 return;
458 }
459 }
460
461 // Hands
462 XrActionStateGetInfo getInfo{};
463 getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
464 for (int i = 0; i < 2; ++i) {
465
466 getInfo.subactionPath = m_handSubactionPath[i];
467 auto &inputState = m_handInputState[i];
468
469 for (const auto &def : m_handInputActionDefs) {
470 getInfo.action = m_inputActions[def.id];
471 switch (def.type) {
472 case XR_ACTION_TYPE_BOOLEAN_INPUT: {
473 XrActionStateBoolean boolValue{};
474 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
475 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
476 if (boolValue.isActive && boolValue.changedSinceLastSync) {
477 //qDebug() << "ACTION" << i << def.shortName << bool(boolValue.currentState);
478 m_handInputState[i]->setInputValue(def.id, def.shortName, float(boolValue.currentState));
479 }
480 } else {
481 qWarning("Failed to get action state for bool hand input");
482 }
483 break;
484 }
485 case XR_ACTION_TYPE_FLOAT_INPUT: {
486 XrActionStateFloat floatValue{};
487 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
488 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
489 if (floatValue.isActive && floatValue.changedSinceLastSync) {
490 //qDebug() << "ACTION" << i << def.shortName << floatValue.currentState;
491 m_handInputState[i]->setInputValue(def.id, def.shortName, float(floatValue.currentState));
492 }
493 } else {
494 qWarning("Failed to get action state for float hand input");
495 }
496 break;
497 }
498 case XR_ACTION_TYPE_VECTOR2F_INPUT:
499 case XR_ACTION_TYPE_POSE_INPUT:
500 case XR_ACTION_TYPE_VIBRATION_OUTPUT:
501 case XR_ACTION_TYPE_MAX_ENUM:
502 break;
503 }
504 }
505
506 // Get pose activity status
507 getInfo.action = m_handActions.gripPoseAction;
508 XrActionStatePose poseState{};
509 poseState.type = XR_TYPE_ACTION_STATE_POSE;
510 if (checkXrResult(xrGetActionStatePose(m_session, &getInfo, &poseState)))
511 inputState->setIsActive(poseState.isActive);
512 else
513 qWarning("Failed to get action state pose");
514
515 // TODO handle any output as well here (haptics)
516 // XrAction gripPoseAction{XR_NULL_HANDLE};
517 // XrAction aimPoseAction{XR_NULL_HANDLE};
518 // XrAction hapticAction{XR_NULL_HANDLE};
519
520 }
521
522}
523
524void QQuick3DXrInputManagerPrivate::updatePoses(XrTime predictedDisplayTime, XrSpace appSpace)
525{
526 // Update the Hands pose
527
528 for (auto hand : {Hand::LeftHand, Hand::RightHand}) {
529 XrSpaceLocation spaceLocation{};
530 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
531 XrResult res;
532 res = xrLocateSpace(handSpace(hand), appSpace, predictedDisplayTime, &spaceLocation);
533 // qDebug() << "LOCATE SPACE hand:" << hand << "res" << res << "flags" << spaceLocation.locationFlags
534 // << "active" << m_handInputState[hand]->isActive()
535 // << "Pos" << spaceLocation.pose.position.x << spaceLocation.pose.position.y << spaceLocation.pose.position.z;
536 m_validAimStateFromUpdatePoses[hand] = m_handInputState[hand]->poseSpace() == QQuick3DXrHandInput::HandPoseSpace::AimPose
537 && XR_UNQUALIFIED_SUCCESS(res) && (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
538 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT); // ### Workaround for Quest issue with hand interaction aim pose
539
540 if (XR_UNQUALIFIED_SUCCESS(res)) {
541 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
542 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
543
544 // Update hand transform
545 setPosePosition(hand, QVector3D(spaceLocation.pose.position.x,
546 spaceLocation.pose.position.y,
547 spaceLocation.pose.position.z) * 100.0f);
548 setPoseRotation(hand, QQuaternion(spaceLocation.pose.orientation.w,
549 spaceLocation.pose.orientation.x,
550 spaceLocation.pose.orientation.y,
551 spaceLocation.pose.orientation.z));
552 }
553 } else {
554 // Tracking loss is expected when the hand is not active so only log a message
555 // if the hand is active.
556 if (isHandActive(hand)) {
557 const char* handName[] = {"left", "right"};
558 qDebug("Unable to locate %s hand action space in app space: %d", handName[hand], res);
559 }
560 }
561 }
562}
563
564void QQuick3DXrInputManagerPrivate::updateHandtracking(XrTime predictedDisplayTime, XrSpace appSpace, bool aimExtensionEnabled)
565{
567
568 XrHandTrackingAimStateFB aimState[2] = {{}, {}}; // Only used when aim extension is enabled
569 XrHandJointVelocitiesEXT velocities[2]{{}, {}};
570 XrHandJointLocationsEXT locations[2]{{}, {}};
571 XrHandJointsLocateInfoEXT locateInfo[2] = {{}, {}};
572
573 for (auto hand : {Hand::LeftHand, Hand::RightHand}) {
574 if (handTracker[hand] == XR_NULL_HANDLE)
575 continue;
576
577 aimState[hand].type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
578
579 velocities[hand].type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
580 velocities[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
581 velocities[hand].jointVelocities = jointVelocities[hand];
582 velocities[hand].next = aimExtensionEnabled ? &aimState[hand] : nullptr;
583
584 locations[hand].type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
585 locations[hand].next = &velocities[hand];
586 locations[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
587 locations[hand].jointLocations = jointLocations[hand];
588
589 locateInfo[hand].type = XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT;
590 locateInfo[hand].baseSpace = appSpace;
591 locateInfo[hand].time = predictedDisplayTime;
592 if (!checkXrResult(xrLocateHandJointsEXT_(handTracker[hand], &locateInfo[hand], &locations[hand])))
593 qWarning("Failed to locate hand joints for hand tracker");
594
595 QList<QVector3D> jp;
596 jp.reserve(XR_HAND_JOINT_COUNT_EXT);
597 QList<QQuaternion> jr;
598 jr.reserve(XR_HAND_JOINT_COUNT_EXT);
599 for (uint i = 0; i < locations[hand].jointCount; ++i) {
600 auto &pose = jointLocations[hand][i].pose;
601 jp.append(OpenXRHelpers::toQVector(pose.position));
602 jr.append(OpenXRHelpers::toQQuaternion(pose.orientation));
603 }
604 m_handInputState[hand]->setJointPositionsAndRotations(jp, jr);
605 m_handInputState[hand]->setIsHandTrackingActive(locations[hand].isActive);
606 }
607
608 if (aimExtensionEnabled) {
609 // Finger pinch handling
610 for (auto hand : {Hand::LeftHand, Hand::RightHand}) {
611 const uint state = aimState[hand].status;
612 const uint oldState = m_aimStateFlags[hand];
613 auto updateState = [&](const char *name, QQuick3DXrInputAction::Action id, uint flag) {
614 if ((state & flag) != (oldState & flag))
615 m_handInputState[hand]->setInputValue(id, name, float(!!(state & flag)));
616 };
617
618 updateState("index_pinch", QQuick3DXrInputAction::IndexFingerPinch, XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB);
619 updateState("middle_pinch", QQuick3DXrInputAction::MiddleFingerPinch, XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB);
620 updateState("ring_pinch", QQuick3DXrInputAction::RingFingerPinch, XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB);
621 updateState("little_pinch", QQuick3DXrInputAction::LittleFingerPinch, XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB);
622 updateState("hand_tracking_menu_press", QQuick3DXrInputAction::HandTrackingMenuPress, XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB);
623 m_aimStateFlags[hand] = state;
624 }
625
626 // ### Workaround for Quest issue with hand interaction aim pose
627 for (auto hand : {Hand::LeftHand, Hand::RightHand}) {
628 if (!m_validAimStateFromUpdatePoses[hand]) {
629 if ((aimState[hand].status & XR_HAND_TRACKING_AIM_VALID_BIT_FB) && m_handInputState[hand]->poseSpace() == QQuick3DXrHandInput::HandPoseSpace::AimPose) {
630 setPosePosition(hand, QVector3D(aimState[hand].aimPose.position.x,
631 aimState[hand].aimPose.position.y,
632 aimState[hand].aimPose.position.z) * 100.0f);
633 setPoseRotation(hand, QQuaternion(aimState[hand].aimPose.orientation.w,
634 aimState[hand].aimPose.orientation.x,
635 aimState[hand].aimPose.orientation.y,
636 aimState[hand].aimPose.orientation.z));
637 m_handInputState[hand]->setIsActive(true); // TODO: clean up
638 }
639 }
640 }
641 }
642 }
643}
644
645void QQuick3DXrInputManagerPrivate::setupHandTracking()
646{
647 resolveXrFunction(
648 "xrCreateHandTrackerEXT",
649 (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_));
650 resolveXrFunction(
651 "xrDestroyHandTrackerEXT",
652 (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_));
653 resolveXrFunction(
654 "xrLocateHandJointsEXT",
655 (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_));
656 resolveXrFunction(
657 "xrGetHandMeshFB",
658 (PFN_xrVoidFunction*)(&xrGetHandMeshFB_));
659
661 XrHandTrackerCreateInfoEXT createInfo{};
662 createInfo.type = XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT;
663 createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
664 createInfo.hand = XR_HAND_LEFT_EXT;
665 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::LeftHand])))
666 qWarning("Failed to create left hand tracker");
667 createInfo.hand = XR_HAND_RIGHT_EXT;
668 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::RightHand])))
669 qWarning("Failed to create right hand tracker");
670 }
671 if (xrGetHandMeshFB_) {
672 for (auto hand : {Hand::LeftHand, Hand::RightHand}) {
673 if (queryHandMesh(hand))
674 createHandModelData(hand);
675 }
676 }
677}
678
679bool QQuick3DXrInputManagerPrivate::queryHandMesh(Hand hand)
680{
681 XrHandTrackingMeshFB mesh {};
682 mesh.type = XR_TYPE_HAND_TRACKING_MESH_FB;
683 // Left hand
684 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
685 qWarning("Failed to query hand mesh info.");
686 return false;
687 }
688
689 mesh.jointCapacityInput = mesh.jointCountOutput;
690 mesh.vertexCapacityInput = mesh.vertexCountOutput;
691 mesh.indexCapacityInput = mesh.indexCountOutput;
692 m_handMeshData[hand].vertexPositions.resize(mesh.vertexCapacityInput);
693 m_handMeshData[hand].vertexNormals.resize(mesh.vertexCapacityInput);
694 m_handMeshData[hand].vertexUVs.resize(mesh.vertexCapacityInput);
695 m_handMeshData[hand].vertexBlendIndices.resize(mesh.vertexCapacityInput);
696 m_handMeshData[hand].vertexBlendWeights.resize(mesh.vertexCapacityInput);
697 m_handMeshData[hand].indices.resize(mesh.indexCapacityInput);
698 mesh.jointBindPoses = m_handMeshData[hand].jointBindPoses;
699 mesh.jointParents = m_handMeshData[hand].jointParents;
700 mesh.jointRadii = m_handMeshData[hand].jointRadii;
701 mesh.vertexPositions = m_handMeshData[hand].vertexPositions.data();
702 mesh.vertexNormals = m_handMeshData[hand].vertexNormals.data();
703 mesh.vertexUVs = m_handMeshData[hand].vertexUVs.data();
704 mesh.vertexBlendIndices = m_handMeshData[hand].vertexBlendIndices.data();
705 mesh.vertexBlendWeights = m_handMeshData[hand].vertexBlendWeights.data();
706 mesh.indices = m_handMeshData[hand].indices.data();
707
708 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
709 qWarning("Failed to get hand mesh data.");
710 return false;
711 }
712
713 return true;
714};
715
716void QQuick3DXrInputManagerPrivate::setupActions()
717{
718 m_handInputActionDefs = {
719 { QQuick3DXrInputAction::Button1Pressed, "b1_pressed", "Button 1 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
720 { QQuick3DXrInputAction::Button1Touched, "b1_touched", "Button 1 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
721 { QQuick3DXrInputAction::Button2Pressed, "b2_pressed", "Button 2 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
722 { QQuick3DXrInputAction::Button2Touched, "b2_touched", "Button 2 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
723 { QQuick3DXrInputAction::ButtonMenuPressed, "bmenu_pressed", "Button Menu Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
724 { QQuick3DXrInputAction::ButtonMenuTouched, "bmenu_touched", "Button Menu Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
725 { QQuick3DXrInputAction::ButtonSystemPressed, "bsystem_pressed", "Button System Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
726 { QQuick3DXrInputAction::ButtonSystemTouched, "bsystem_touched", "Button System Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
727 { QQuick3DXrInputAction::SqueezeValue, "squeeze_value", "Squeeze Value", XR_ACTION_TYPE_FLOAT_INPUT },
728 { QQuick3DXrInputAction::SqueezeForce, "squeeze_force", "Squeeze Force", XR_ACTION_TYPE_FLOAT_INPUT },
729 { QQuick3DXrInputAction::SqueezePressed, "squeeze_pressed", "Squeeze Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
730 { QQuick3DXrInputAction::TriggerValue, "trigger_value", "Trigger Value", XR_ACTION_TYPE_FLOAT_INPUT },
731 { QQuick3DXrInputAction::TriggerPressed, "trigger_pressed", "Trigger Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
732 { QQuick3DXrInputAction::TriggerTouched, "trigger_touched", "Trigger Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
733 { QQuick3DXrInputAction::ThumbstickX, "thumbstick_x", "Thumbstick X", XR_ACTION_TYPE_FLOAT_INPUT },
734 { QQuick3DXrInputAction::ThumbstickY, "thumbstick_y", "Thumbstick Y", XR_ACTION_TYPE_FLOAT_INPUT },
735 { QQuick3DXrInputAction::ThumbstickPressed, "thumbstick_pressed", "Thumbstick Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
736 { QQuick3DXrInputAction::ThumbstickTouched, "thumbstick_touched", "Thumbstick Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
737 { QQuick3DXrInputAction::ThumbrestTouched, "thumbrest_touched", "Thumbrest Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
738 { QQuick3DXrInputAction::TrackpadX, "trackpad_x", "Trackpad X", XR_ACTION_TYPE_FLOAT_INPUT },
739 { QQuick3DXrInputAction::TrackpadY, "trackpad_y", "Trackpad Y", XR_ACTION_TYPE_FLOAT_INPUT },
740 { QQuick3DXrInputAction::TrackpadForce, "trackpad_force", "Trackpad Force", XR_ACTION_TYPE_FLOAT_INPUT },
741 { QQuick3DXrInputAction::TrackpadTouched, "trackpad_touched", "Trackpad Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
742 { QQuick3DXrInputAction::TrackpadPressed, "trackpad_pressed", "Trackpad Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT }
743 };
744
745 // Create an action set.
746 {
747 XrActionSetCreateInfo actionSetInfo{};
748 actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
749 strcpy(actionSetInfo.actionSetName, "gameplay");
750 strcpy(actionSetInfo.localizedActionSetName, "Gameplay");
751 actionSetInfo.priority = 0;
752 if (!checkXrResult(xrCreateActionSet(m_instance, &actionSetInfo, &m_actionSet)))
753 qWarning("Failed to create gameplay action set");
754 }
755
756 // Create Hand Actions
757 setPath(m_handSubactionPath[0], "/user/hand/left");
758 setPath(m_handSubactionPath[1], "/user/hand/right");
759
760 for (const auto &def : m_handInputActionDefs) {
761 createAction(def.type,
762 def.shortName,
763 def.localizedName,
764 2,
765 m_handSubactionPath,
766 m_inputActions[def.id]);
767 }
768
769 createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT,
770 "vibrate_hand",
771 "Vibrate Hand",
772 2,
773 m_handSubactionPath,
774 m_handActions.hapticAction);
775 createAction(XR_ACTION_TYPE_POSE_INPUT,
776 "hand_grip_pose",
777 "Hand Grip Pose",
778 2,
779 m_handSubactionPath,
780 m_handActions.gripPoseAction);
781 createAction(XR_ACTION_TYPE_POSE_INPUT,
782 "hand_aim_pose",
783 "Hand Aim Pose",
784 2,
785 m_handSubactionPath,
786 m_handActions.aimPoseAction);
787
788}
789
790void QQuick3DXrInputManagerPrivate::destroyActions()
791{
792 for (auto &action : m_inputActions) {
793 if (action)
794 xrDestroyAction(action);
795 }
796
797 xrDestroyAction(m_handActions.gripPoseAction);
798 xrDestroyAction(m_handActions.aimPoseAction);
799 xrDestroyAction(m_handActions.hapticAction);
800
801 xrDestroyActionSet(m_actionSet);
802}
803
804bool QQuick3DXrInputManagerPrivate::checkXrResult(const XrResult &result)
805{
806 return OpenXRHelpers::checkXrResult(result, m_instance);
807}
808
809bool QQuick3DXrInputManagerPrivate::resolveXrFunction(const char *name, PFN_xrVoidFunction *function)
810{
811 XrResult result = xrGetInstanceProcAddr(m_instance, name, function);
812 if (!OpenXRHelpers::checkXrResult(result, m_instance)) {
813 qWarning("Failed to resolve OpenXR function %s", name);
814 *function = nullptr;
815 return false;
816 }
817 return true;
818}
819
820void QQuick3DXrInputManagerPrivate::setPath(XrPath &path, const QByteArray &pathString)
821{
822 if (!checkXrResult(xrStringToPath(m_instance, pathString.constData(), &path)))
823 qWarning("xrStringToPath failed");
824}
825
826void QQuick3DXrInputManagerPrivate::createAction(XrActionType type,
827 const char *name,
828 const char *localizedName,
829 int numSubactions,
830 XrPath *subactionPath,
831 XrAction &action)
832{
833 XrActionCreateInfo actionInfo{};
834 actionInfo.type = XR_TYPE_ACTION_CREATE_INFO;
835 actionInfo.actionType = type;
836 strcpy(actionInfo.actionName, name);
837 strcpy(actionInfo.localizedActionName, localizedName);
838 actionInfo.countSubactionPaths = quint32(numSubactions);
839 actionInfo.subactionPaths = subactionPath;
840 if (!checkXrResult(xrCreateAction(m_actionSet, &actionInfo, &action)))
841 qDebug() << "xrCreateAction failed. Name:" << name << "localizedName:" << localizedName;
842}
843
844void QQuick3DXrInputManagerPrivate::getBoolInputState(XrActionStateGetInfo &getInfo, const XrAction &action, std::function<void(bool)> setter)
845{
846 getInfo.action = action;
847 XrActionStateBoolean boolValue{};
848 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
849 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
850 if (boolValue.isActive == XR_TRUE)
851 setter(bool(boolValue.currentState));
852 } else {
853 qWarning("Failed to get action state: bool");
854 }
855}
856
857void QQuick3DXrInputManagerPrivate::getFloatInputState(XrActionStateGetInfo &getInfo, const XrAction &action, std::function<void(float)> setter)
858{
859 getInfo.action = action;
860 XrActionStateFloat floatValue{};
861 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
862 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
863 if (floatValue.isActive == XR_TRUE)
864 setter(float(floatValue.currentState));
865 } else {
866 qWarning("Failed to get action state: float");
867 }
868}
869
871{
872 if (m_handInputState[hand]->poseSpace() == QQuick3DXrHandInput::HandPoseSpace::GripPose)
873 return m_handGripSpace[hand];
874 else
875 return m_handAimSpace[hand];
876}
877
879{
880 return m_handInputState[hand]->isActive();
881}
882
884{
885 return m_handInputState[hand]->isHandTrackingActive();
886}
887
889{
890 m_handInputState[hand]->setPosePosition(position); // TODO: move to controller!!!
891}
892
894{
895 m_handInputState[hand]->setPoseRotation(rotation);
896}
897
899{
900 return m_handInputState[Hand::LeftHand];
901}
902
904{
905 return m_handInputState[Hand::RightHand];
906}
907
908static inline QMatrix4x4 transformMatrix(const QVector3D &position, const QQuaternion &rotation)
909{
910 QMatrix4x4 transform = QMatrix4x4{rotation.toRotationMatrix()};
911
912 transform(0, 3) += position[0];
913 transform(1, 3) += position[1];
914 transform(2, 3) += position[2];
915
916 return transform;
917}
918
919void QQuick3DXrInputManagerPrivate::setupHandModelInternal(QQuick3DXrHandModel *model, Hand hand)
920{
921 QQuick3DGeometry *geometry = m_handGeometryData[hand].geometry;
922 if (!geometry)
923 return;
924
925 model->setGeometry(geometry);
926
927 QQuick3DSkin *skin = new QQuick3DSkin(model);
928 auto jointListProp = skin->joints();
929 QList<QMatrix4x4> inverseBindPoses;
930 inverseBindPoses.reserve(XR_HAND_JOINT_COUNT_EXT);
931
932 const auto &handMeshData = m_handMeshData[hand];
933
934 for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) {
935 const auto &pose = handMeshData.jointBindPoses[i];
936 const QVector3D pos = OpenXRHelpers::toQVector(pose.position);
937 const QQuaternion rot = OpenXRHelpers::toQQuaternion(pose.orientation);
938 inverseBindPoses.append(transformMatrix(pos, rot).inverted());
939 QQuick3DNode *joint = new QQuick3DNode(model);
940 joint->setPosition(pos);
941 joint->setRotation(rot);
942 jointListProp.append(&jointListProp, joint);
943 }
944 skin->setInverseBindPoses(inverseBindPoses);
945 model->setSkin(skin);
946}
947
949{
950 QSSG_ASSERT(model != nullptr, return);
951
952 if (model->geometry() != nullptr || model->skin() != nullptr) {
953 qWarning() << "Hand model already has geometry or skin set.";
954 return;
955 }
956
957 auto hand = model->hand();
959 setupHandModelInternal(model, Hand::LeftHand);
960 else if (hand == QQuick3DXrHandModel::RightHand)
961 setupHandModelInternal(model, Hand::RightHand);
962 else
963 qWarning() << "No matching hand tracker input found for hand model.";
964}
965
966void QQuick3DXrInputManagerPrivate::createHandModelData(Hand hand)
967{
968 const auto &handMeshData = m_handMeshData[hand];
969
970 auto &geometry = m_handGeometryData[hand].geometry;
971 delete geometry;
972 geometry = createHandMeshGeometry(handMeshData);
973}
974
bool isActive
\inmodule QtCore
Definition qbytearray.h:57
void reserve(qsizetype size)
Attempts to allocate memory for at least size bytes.
Definition qbytearray.h:635
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
The QQuaternion class represents a quaternion consisting of a vector and scalar.
\qmltype Geometry \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DGeometry
void setPrimitiveType(PrimitiveType type)
Sets the primitive type used for rendering to type.
void setStride(int stride)
Sets the stride of the vertex buffer to stride, measured in bytes.
void addAttribute(Attribute::Semantic semantic, int offset, Attribute::ComponentType componentType)
Adds vertex attribute description.
void setVertexData(const QByteArray &data)
Sets the vertex buffer data.
void setBounds(const QVector3D &min, const QVector3D &max)
Sets the bounding volume of the geometry to the cube defined by the points min and max.
void setIndexData(const QByteArray &data)
Sets the index buffer to data.
void setInverseBindPoses(const QList< QMatrix4x4 > &poses)
QQmlListProperty< QQuick3DNode > joints
\qmlproperty List<QtQuick3D::Node> Skin::joints
void setIsHandTrackingActive(bool newIsHandTracking)
void setIsActive(bool isActive)
void setPoseRotation(const QQuaternion &rotation)
void setInputValue(int id, const char *shortName, float value)
void setJointPositionsAndRotations(const QList< QVector3D > &newJointPositions, const QList< QQuaternion > &newJointRotations)
HandPoseSpace poseSpace() const
void setPosePosition(const QVector3D &position)
void init(XrInstance instance, XrSession session)
XrHandJointVelocityEXT jointVelocities[2][XR_HAND_JOINT_COUNT_EXT]
QQuick3DXrInputManagerPrivate(QQuick3DXrInputManager &manager)
void updatePoses(XrTime predictedDisplayTime, XrSpace appSpace)
PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_
QQuick3DXrHandInput * rightHandInput() const
static QQuick3DXrInputManagerPrivate * get(QQuick3DXrInputManager *inputManager)
void setPosePosition(Hand hand, const QVector3D &position)
void setPoseRotation(Hand hand, const QQuaternion &rotation)
void setupHandModel(QQuick3DXrHandModel *model)
PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_
QQuick3DXrHandInput * leftHandInput() const
XrHandJointLocationEXT jointLocations[2][XR_HAND_JOINT_COUNT_EXT]
PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
else opt state
[0]
QQuaternion toQQuaternion(const XrQuaternionf &q)
bool checkXrResult(XrResult result, XrInstance instance)
QVector3D toQVector(const XrVector3f &v)
Combined button and popup list for selecting options.
void setter(QUntypedPropertyData *d, const void *value)
QString boolValue(bool v)
Definition language.cpp:493
#define qDebug
[1]
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:167
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
GLuint index
[2]
GLenum GLuint id
[7]
GLfloat GLfloat f
GLuint GLuint GLfloat weight
GLenum GLsizei const void * pathString
const void GLsizei GLsizei stride
GLenum type
GLenum GLuint GLintptr offset
GLuint name
GLuint GLenum GLenum transform
GLuint res
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLuint const GLint * locations
static QMatrix4x4 transformMatrix(const QVector3D &position, const QQuaternion &rotation)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define QSSG_ASSERT(cond, action)
unsigned int quint32
Definition qtypes.h:50
int qint32
Definition qtypes.h:49
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
QSqlQueryModel * model
[16]
QFileSelector selector
[1]
QNetworkAccessManager manager