4#include "../qquick3dxrinputmanager_p.h"
6#include "openxr/qopenxrhelpers_p.h"
14#include <private/qquick3djoint_p.h>
16#include <QtGui/qquaternion.h>
20Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
25 m_handInputState[Hand::LeftHand] =
new QQuick3DXrHandInput(
this);
26 m_handInputState[Hand::RightHand] =
new QQuick3DXrHandInput(
this);
32 delete m_handInputState[Hand::LeftHand];
33 delete m_handInputState[Hand::RightHand];
35 m_handInputState[Hand::LeftHand] =
nullptr;
36 m_handInputState[Hand::RightHand] =
nullptr;
41 QXRHandComponentPath res;
42 setPath(res.paths[Hand::LeftHand],
"/user/hand/left/" + path);
43 setPath(res.paths[Hand::RightHand],
"/user/hand/right/" + path);
51 setPath(res, path.toByteArray());
57 QQuick3DGeometry *geometry =
new QQuick3DGeometry();
58 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
61 const qsizetype expectedLength = handMeshData.vertexPositions.size();
62 bool hasPositions = !handMeshData.vertexPositions.isEmpty();
63 bool hasNormals = handMeshData.vertexNormals.size() >= expectedLength;
64 bool hasUV0s = handMeshData.vertexUVs.size() >= expectedLength;
65 bool hasJoints = handMeshData.vertexBlendIndices.size() >= expectedLength;
66 bool hasWeights = handMeshData.vertexBlendWeights.size() >= expectedLength;
67 bool hasIndexes = !handMeshData.indices.isEmpty();
71 geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
72 offset += 3 *
sizeof(
float);
76 geometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
77 offset += 3 *
sizeof(
float);
81 geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
82 offset += 2 *
sizeof(
float);
86 geometry->addAttribute(QQuick3DGeometry::Attribute::JointSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::I32Type);
87 offset += 4 *
sizeof(qint32);
91 geometry->addAttribute(QQuick3DGeometry::Attribute::WeightSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
92 offset += 4 *
sizeof(
float);
96 geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U16Type);
99 const int stride = offset;
100 const qsizetype bufferSize = expectedLength * stride;
101 geometry->setStride(stride);
103 QByteArray vertexBuffer;
104 vertexBuffer.reserve(bufferSize);
109 auto appendFloat = [&vertexBuffer](
float f) {
110 vertexBuffer.append(
reinterpret_cast<
const char *>(&f),
sizeof(
float));
112 auto appendInt = [&vertexBuffer](qint32 i) {
113 vertexBuffer.append(
reinterpret_cast<
const char *>(&i),
sizeof(qint32));
116 for (qsizetype i = 0; i < expectedLength; ++i) {
119 const QVector3D position = OpenXRHelpers::toQVector(handMeshData.vertexPositions[i]);
120 appendFloat(position.x());
121 appendFloat(position.y());
122 appendFloat(position.z());
123 minBounds.setX(qMin(minBounds.x(), position.x()));
124 maxBounds.setX(qMax(maxBounds.x(), position.x()));
125 minBounds.setY(qMin(minBounds.y(), position.y()));
126 maxBounds.setY(qMax(maxBounds.y(), position.y()));
127 minBounds.setZ(qMin(minBounds.z(), position.z()));
128 maxBounds.setZ(qMax(maxBounds.z(), position.z()));
131 const auto &normal = handMeshData.vertexNormals[i];
132 appendFloat(normal.x);
133 appendFloat(normal.y);
134 appendFloat(normal.z);
138 const auto &uv0 = handMeshData.vertexUVs[i];
144 const auto &joint = handMeshData.vertexBlendIndices[i];
152 const auto &weight = handMeshData.vertexBlendWeights[i];
153 appendFloat(weight.x);
154 appendFloat(weight.y);
155 appendFloat(weight.z);
156 appendFloat(weight.w);
160 geometry->setBounds(minBounds, maxBounds);
161 geometry->setVertexData(vertexBuffer);
165 const qsizetype indexLength = handMeshData.indices.size();
166 QByteArray indexBuffer;
167 indexBuffer.reserve(indexLength *
sizeof(int16_t));
168 for (qsizetype i = 0; i < indexLength; ++i) {
169 const auto &index = handMeshData.indices[i];
170 indexBuffer.append(
reinterpret_cast<
const char *>(&index),
sizeof(int16_t));
172 geometry->setIndexData(indexBuffer);
181 qWarning() <<
"QQuick3DXrInputManager: Trying to initialize an already initialized session";
185 m_instance = instance;
192 QXRHandComponentPath aClick = makeHandInputPaths(
"input/a/click");
193 QXRHandComponentPath bClick = makeHandInputPaths(
"input/b/click");
194 QXRHandComponentPath aTouch = makeHandInputPaths(
"input/a/touch");
195 QXRHandComponentPath bTouch = makeHandInputPaths(
"input/b/touch");
197 QXRHandComponentPath xClick = makeHandInputPaths(
"input/x/click");
198 QXRHandComponentPath yClick = makeHandInputPaths(
"input/y/click");
199 QXRHandComponentPath xTouch = makeHandInputPaths(
"input/x/touch");
200 QXRHandComponentPath yTouch = makeHandInputPaths(
"input/y/touch");
202 QXRHandComponentPath menuClick = makeHandInputPaths(
"input/menu/click");
203 QXRHandComponentPath systemClick = makeHandInputPaths(
"input/system/click");
204 QXRHandComponentPath systemTouch = makeHandInputPaths(
"input/system/touch");
206 QXRHandComponentPath squeezeValue = makeHandInputPaths(
"input/squeeze/value");
207 QXRHandComponentPath squeezeForce = makeHandInputPaths(
"input/squeeze/force");
208 QXRHandComponentPath squeezeClick = makeHandInputPaths(
"input/squeeze/click");
210 QXRHandComponentPath triggerValue = makeHandInputPaths(
"input/trigger/value");
211 QXRHandComponentPath triggerTouch = makeHandInputPaths(
"input/trigger/touch");
212 QXRHandComponentPath triggerClick = makeHandInputPaths(
"input/trigger/click");
214 QXRHandComponentPath thumbstickX = makeHandInputPaths(
"input/thumbstick/x");
215 QXRHandComponentPath thumbstickY = makeHandInputPaths(
"input/thumbstick/y");
216 QXRHandComponentPath thumbstickClick = makeHandInputPaths(
"input/thumbstick/click");
217 QXRHandComponentPath thumbstickTouch = makeHandInputPaths(
"input/thumbstick/touch");
218 QXRHandComponentPath thumbrestTouch = makeHandInputPaths(
"input/thumbrest/touch");
220 QXRHandComponentPath trackpadX = makeHandInputPaths(
"input/trackpad/x");
221 QXRHandComponentPath trackpadY = makeHandInputPaths(
"input/trackpad/y");
222 QXRHandComponentPath trackpadForce = makeHandInputPaths(
"input/trackpad/force");
223 QXRHandComponentPath trackpadClick = makeHandInputPaths(
"input/trackpad/click");
224 QXRHandComponentPath trackpadTouch = makeHandInputPaths(
"input/trackpad/touch");
226 XrPath handLeftGripPose;
227 XrPath handLeftAimPose;
228 XrPath handLeftHaptic;
230 XrPath handRightGripPose;
231 XrPath handRightAimPose;
232 XrPath handRightHaptic;
236 setPath(handLeftGripPose,
"/user/hand/left/input/grip/pose");
237 setPath(handLeftAimPose,
"/user/hand/left/input/aim/pose");
238 setPath(handLeftHaptic,
"/user/hand/left/output/haptic");
240 setPath(handRightGripPose,
"/user/hand/right/input/grip/pose");
241 setPath(handRightAimPose,
"/user/hand/right/input/aim/pose");
242 setPath(handRightHaptic,
"/user/hand/right/output/haptic");
246 using XrActionBindings = std::vector<XrActionSuggestedBinding>;
247 using HandInputMapping = std::vector<std::tuple<QQuick3DXrInputAction::Action, QXRHandComponentPath, SubPathSelector>>;
248 auto addToBindings = [
this](XrActionBindings &bindings,
const HandInputMapping &defs){
249 for (
const auto &[actionId, path, selector] : defs) {
250 if (selector & LeftHandSubPath)
251 bindings.push_back({ m_inputActions[actionId], path.paths[Hand::LeftHand] });
252 if (selector & RightHandSubPath)
253 bindings.push_back({ m_inputActions[actionId], path.paths[Hand::RightHand] });
259 HandInputMapping mappingDefs {
260 { QQuick3DXrInputAction::Button1Pressed, xClick, LeftHandSubPath },
261 { QQuick3DXrInputAction::Button1Pressed, aClick, RightHandSubPath },
262 { QQuick3DXrInputAction::Button2Pressed, yClick, LeftHandSubPath },
263 { QQuick3DXrInputAction::Button2Pressed, bClick, RightHandSubPath },
264 { QQuick3DXrInputAction::Button1Touched, xTouch, LeftHandSubPath },
265 { QQuick3DXrInputAction::Button1Touched, aTouch, RightHandSubPath },
266 { QQuick3DXrInputAction::Button2Touched, yTouch, LeftHandSubPath },
267 { QQuick3DXrInputAction::Button2Touched, bTouch, RightHandSubPath },
268 { QQuick3DXrInputAction::ButtonMenuPressed, menuClick, LeftHandSubPath },
269 { QQuick3DXrInputAction::ButtonSystemPressed, systemClick, RightHandSubPath },
270 { QQuick3DXrInputAction::SqueezeValue, squeezeValue, BothHandsSubPath },
271 { QQuick3DXrInputAction::TriggerValue, triggerValue, BothHandsSubPath },
272 { QQuick3DXrInputAction::TriggerTouched, triggerTouch, BothHandsSubPath },
273 { QQuick3DXrInputAction::ThumbstickX, thumbstickX, BothHandsSubPath },
274 { QQuick3DXrInputAction::ThumbstickY, thumbstickY, BothHandsSubPath },
275 { QQuick3DXrInputAction::ThumbstickPressed, thumbstickClick, BothHandsSubPath },
276 { QQuick3DXrInputAction::ThumbstickTouched, thumbstickTouch, BothHandsSubPath },
277 { QQuick3DXrInputAction::ThumbrestTouched, thumbrestTouch, BothHandsSubPath },
280 XrPath oculusTouchProfile;
281 setPath(oculusTouchProfile,
"/interaction_profiles/oculus/touch_controller");
282 std::vector<XrActionSuggestedBinding> bindings {{
283 {m_handActions.gripPoseAction, handLeftGripPose},
284 {m_handActions.aimPoseAction, handLeftAimPose},
285 {m_handActions.hapticAction, handLeftHaptic},
287 {m_handActions.gripPoseAction, handRightGripPose},
288 {m_handActions.aimPoseAction, handRightAimPose},
289 {m_handActions.hapticAction, handRightHaptic},
292 addToBindings(bindings, mappingDefs);
294 XrInteractionProfileSuggestedBinding suggestedBindings{};
295 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
296 suggestedBindings.interactionProfile = oculusTouchProfile;
297 suggestedBindings.suggestedBindings = bindings.data();
298 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
299 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
300 qWarning(
"Failed to get suggested interaction profile bindings for Oculus touch");
306 XrPath handInteractionProfile;
307 setPath(handInteractionProfile,
"/interaction_profiles/microsoft/hand_interaction");
308 std::vector<XrActionSuggestedBinding> bindings {{
309 {m_handActions.gripPoseAction, handLeftGripPose},
310 {m_handActions.aimPoseAction, handLeftAimPose},
311 {m_handActions.gripPoseAction, handRightGripPose},
312 {m_handActions.aimPoseAction, handRightAimPose},
315 HandInputMapping mappingDefs {
316 { QQuick3DXrInputAction::SqueezeValue, squeezeValue, BothHandsSubPath },
319 addToBindings(bindings, mappingDefs);
321 XrInteractionProfileSuggestedBinding suggestedBindings{};
322 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
323 suggestedBindings.interactionProfile = handInteractionProfile;
324 suggestedBindings.suggestedBindings = bindings.data();
325 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
327 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
328 qWarning(
"Failed to get suggested interaction profile bindings for MSFT hand interaction");
332 XrPath htcViveProfile;
333 setPath(htcViveProfile,
"/interaction_profiles/htc/vive_controller");
335 HandInputMapping mappingDefs {
336 { QQuick3DXrInputAction::ButtonMenuPressed, menuClick, BothHandsSubPath },
337 { QQuick3DXrInputAction::ButtonSystemPressed, systemClick, BothHandsSubPath },
338 { QQuick3DXrInputAction::SqueezePressed, squeezeClick, BothHandsSubPath },
339 { QQuick3DXrInputAction::TriggerValue, triggerValue, BothHandsSubPath },
340 { QQuick3DXrInputAction::TriggerPressed, triggerClick, BothHandsSubPath },
341 { QQuick3DXrInputAction::TrackpadX, trackpadX, BothHandsSubPath },
342 { QQuick3DXrInputAction::TrackpadY, trackpadY, BothHandsSubPath },
343 { QQuick3DXrInputAction::TrackpadPressed, trackpadClick, BothHandsSubPath },
344 { QQuick3DXrInputAction::TrackpadTouched, trackpadTouch, BothHandsSubPath },
347 std::vector<XrActionSuggestedBinding> bindings {{
348 {m_handActions.gripPoseAction, handLeftGripPose},
349 {m_handActions.aimPoseAction, handLeftAimPose},
350 {m_handActions.hapticAction, handLeftHaptic},
352 {m_handActions.gripPoseAction, handRightGripPose},
353 {m_handActions.aimPoseAction, handRightAimPose},
354 {m_handActions.hapticAction, handRightHaptic},
357 addToBindings(bindings, mappingDefs);
359 XrInteractionProfileSuggestedBinding suggestedBindings{};
360 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
361 suggestedBindings.interactionProfile = htcViveProfile;
362 suggestedBindings.suggestedBindings = bindings.data();
363 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
364 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
365 qWarning(
"Failed to get suggested interaction profile bindings for Vive controller");
370 XrPath microsoftMotionProfile;
371 setPath(microsoftMotionProfile,
"/interaction_profiles/microsoft/motion_controller");
376 XrPath valveIndexProfile;
377 setPath(valveIndexProfile,
"/interaction_profiles/valve/index_controller");
382 XrActionSpaceCreateInfo actionSpaceInfo{};
383 actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
384 actionSpaceInfo.action = m_handActions.gripPoseAction;
385 actionSpaceInfo.poseInActionSpace.orientation.w = 1.0f;
387 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
388 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[0])))
389 qWarning(
"Failed to create action space for handGripSpace[0]");
390 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
391 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[1])))
392 qWarning(
"Failed to create action space for handGripSpace[1]");
394 actionSpaceInfo.action = m_handActions.aimPoseAction;
395 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
396 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[0])))
397 qWarning(
"Failed to create action space for handAimSpace[0]");
398 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
399 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[1])))
400 qWarning(
"Failed to create action space for handAimSpace[1]");
404 XrSessionActionSetsAttachInfo attachInfo{};
405 attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
406 attachInfo.countActionSets = 1;
407 attachInfo.actionSets = &m_actionSet;
408 if (!checkXrResult(xrAttachSessionActionSets(m_session, &attachInfo)))
409 qWarning(
"Failed to attach action sets to session");
411 m_initialized =
true;
419 m_initialized =
false;
421 xrDestroySpace(m_handGripSpace[0]);
422 xrDestroySpace(m_handGripSpace[1]);
423 xrDestroySpace(m_handAimSpace[0]);
424 xrDestroySpace(m_handAimSpace[1]);
428 if (xrDestroyHandTrackerEXT_) {
429 xrDestroyHandTrackerEXT_(handTracker[Hand::LeftHand]);
430 xrDestroyHandTrackerEXT_(handTracker[Hand::RightHand]);
433 m_instance = {XR_NULL_HANDLE};
434 m_session = {XR_NULL_HANDLE};
439 QSSG_ASSERT(inputManager !=
nullptr,
return nullptr);
440 return inputManager->d_func();
449 const XrActiveActionSet activeActionSet{m_actionSet, XR_NULL_PATH};
450 XrActionsSyncInfo syncInfo{};
451 syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO;
452 syncInfo.countActiveActionSets = 1;
453 syncInfo.activeActionSets = &activeActionSet;
454 XrResult result = xrSyncActions(m_session, &syncInfo);
455 if (!(result == XR_SUCCESS ||
456 result == XR_SESSION_LOSS_PENDING ||
457 result == XR_SESSION_NOT_FOCUSED))
459 if (!checkXrResult(result)) {
460 qWarning(
"xrSyncActions failed");
466 XrActionStateGetInfo getInfo{};
467 getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
468 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
470 getInfo.subactionPath = m_handSubactionPath[hand];
471 auto &inputState = m_handInputState[hand];
473 for (
const auto &def : m_handInputActionDefs) {
474 getInfo.action = m_inputActions[def.id];
476 case XR_ACTION_TYPE_BOOLEAN_INPUT: {
477 XrActionStateBoolean boolValue{};
478 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
479 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
480 if (boolValue.isActive && boolValue.changedSinceLastSync) {
482 setInputValue(hand, def.id, def.shortName,
float(boolValue.currentState));
485 qWarning(
"Failed to get action state for bool hand input");
489 case XR_ACTION_TYPE_FLOAT_INPUT: {
490 XrActionStateFloat floatValue{};
491 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
492 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
493 if (floatValue.isActive && floatValue.changedSinceLastSync) {
495 setInputValue(hand, def.id, def.shortName,
float(floatValue.currentState));
498 qWarning(
"Failed to get action state for float hand input");
502 case XR_ACTION_TYPE_VECTOR2F_INPUT:
503 case XR_ACTION_TYPE_POSE_INPUT:
504 case XR_ACTION_TYPE_VIBRATION_OUTPUT:
505 case XR_ACTION_TYPE_MAX_ENUM:
511 getInfo.action = m_handActions.gripPoseAction;
512 XrActionStatePose poseState{};
513 poseState.type = XR_TYPE_ACTION_STATE_POSE;
514 if (checkXrResult(xrGetActionStatePose(m_session, &getInfo, &poseState)))
515 inputState->setIsActive(poseState.isActive);
517 qWarning(
"Failed to get action state pose");
523 const QList<QPointer<QQuick3DXrHapticFeedback>> hapticOutputData = QQuick3DXrActionMapper::getHapticEffects(
static_cast<QQuick3DXrInputAction::Controller>(hand));
525 for (
auto &hapticFeedback : hapticOutputData) {
526 const bool triggered = hapticFeedback->testAndClear();
528 if (
auto *hapticEffect = qobject_cast<QQuick3DXrSimpleHapticEffect*>(hapticFeedback->hapticEffect())) {
529 XrHapticVibration vibration {XR_TYPE_HAPTIC_VIBRATION,
nullptr, 0, 0, 0};
530 vibration.amplitude = hapticEffect->amplitude();
531 vibration.duration = hapticEffect->duration() * 1000000;
532 vibration.frequency = hapticEffect->frequency();
534 XrHapticActionInfo hapticActionInfo {XR_TYPE_HAPTIC_ACTION_INFO,
nullptr, m_handActions.hapticAction, m_handSubactionPath[hand]};
536 if (!checkXrResult(xrApplyHapticFeedback(m_session, &hapticActionInfo, (
const XrHapticBaseHeader*)&vibration))) {
537 qWarning(
"Failed to trigger haptic feedback");
549 for (
auto poseSpace : {HandPoseSpace::AimPose, HandPoseSpace::GripPose}) {
550 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
551 if (!isPoseInUse(hand, poseSpace))
553 XrSpaceLocation spaceLocation{};
554 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
556 res = xrLocateSpace(handSpace(hand, poseSpace), appSpace, predictedDisplayTime, &spaceLocation);
560 m_validAimStateFromUpdatePoses[hand] = poseSpace == HandPoseSpace::AimPose
561 && XR_UNQUALIFIED_SUCCESS(res) && (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
562 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT);
564 if (XR_UNQUALIFIED_SUCCESS(res)) {
565 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
566 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
569 setPosePositionAndRotation(hand, poseSpace,
570 QVector3D(spaceLocation.pose.position.x,
571 spaceLocation.pose.position.y,
572 spaceLocation.pose.position.z) * 100.0f,
573 QQuaternion(spaceLocation.pose.orientation.w,
574 spaceLocation.pose.orientation.x,
575 spaceLocation.pose.orientation.y,
576 spaceLocation.pose.orientation.z));
581 if (isHandActive(hand)) {
582 const char* handName[] = {
"left",
"right"};
583 qCDebug(lcQuick3DXr,
"Unable to locate %s hand action space in app space: %d", handName[hand], res);
592 if (xrLocateHandJointsEXT_) {
594 XrHandTrackingAimStateFB aimState[2] = {{}, {}};
595 XrHandJointVelocitiesEXT velocities[2]{{}, {}};
596 XrHandJointLocationsEXT locations[2]{{}, {}};
597 XrHandJointsLocateInfoEXT locateInfo[2] = {{}, {}};
599 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
600 if (handTracker[hand] == XR_NULL_HANDLE)
603 aimState[hand].type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
605 velocities[hand].type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
606 velocities[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
607 velocities[hand].jointVelocities = jointVelocities[hand];
608 velocities[hand].next = aimExtensionEnabled ? &aimState[hand] :
nullptr;
610 locations[hand].type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
611 locations[hand].next = &velocities[hand];
612 locations[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
613 locations[hand].jointLocations = jointLocations[hand];
615 locateInfo[hand].type = XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT;
616 locateInfo[hand].baseSpace = appSpace;
617 locateInfo[hand].time = predictedDisplayTime;
618 if (!checkXrResult(xrLocateHandJointsEXT_(handTracker[hand], &locateInfo[hand], &locations[hand])))
619 qWarning(
"Failed to locate hand joints for hand tracker");
622 jp.reserve(XR_HAND_JOINT_COUNT_EXT);
623 QList<QQuaternion> jr;
624 jr.reserve(XR_HAND_JOINT_COUNT_EXT);
625 for (uint i = 0; i < locations[hand].jointCount; ++i) {
626 auto &pose = jointLocations[hand][i].pose;
627 jp.append(OpenXRHelpers::toQVector(pose.position));
628 jr.append(OpenXRHelpers::toQQuaternion(pose.orientation));
630 m_handInputState[hand]->setJointPositionsAndRotations(jp, jr);
631 m_handInputState[hand]->setIsHandTrackingActive(locations[hand].isActive);
634 if (aimExtensionEnabled) {
636 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
637 const uint state = aimState[hand].status;
638 const uint oldState = m_aimStateFlags[hand];
639 auto updateState = [&](
const char *name, QQuick3DXrInputAction::Action id, uint flag) {
640 if ((state & flag) != (oldState & flag))
641 setInputValue(hand, id, name,
float(!!(state & flag)));
644 updateState(
"index_pinch", QQuick3DXrInputAction::IndexFingerPinch, XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB);
645 updateState(
"middle_pinch", QQuick3DXrInputAction::MiddleFingerPinch, XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB);
646 updateState(
"ring_pinch", QQuick3DXrInputAction::RingFingerPinch, XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB);
647 updateState(
"little_pinch", QQuick3DXrInputAction::LittleFingerPinch, XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB);
648 updateState(
"hand_tracking_menu_press", QQuick3DXrInputAction::HandTrackingMenuPress, XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB);
649 m_aimStateFlags[hand] = state;
653 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
654 if (isPoseInUse(hand, HandPoseSpace::AimPose) && !m_validAimStateFromUpdatePoses[hand]) {
655 if ((aimState[hand].status & XR_HAND_TRACKING_AIM_VALID_BIT_FB)) {
656 setPosePositionAndRotation(hand, HandPoseSpace::AimPose,
657 QVector3D(aimState[hand].aimPose.position.x,
658 aimState[hand].aimPose.position.y,
659 aimState[hand].aimPose.position.z) * 100.0f,
660 QQuaternion(aimState[hand].aimPose.orientation.w,
661 aimState[hand].aimPose.orientation.x,
662 aimState[hand].aimPose.orientation.y,
663 aimState[hand].aimPose.orientation.z));
664 m_handInputState[hand]->setIsActive(
true);
674 OpenXRHelpers::resolveXrFunction(
676 "xrCreateHandTrackerEXT",
677 (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_));
678 OpenXRHelpers::resolveXrFunction(
680 "xrDestroyHandTrackerEXT",
681 (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_));
682 OpenXRHelpers::resolveXrFunction(
684 "xrLocateHandJointsEXT",
685 (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_));
686 OpenXRHelpers::resolveXrFunction(
689 (PFN_xrVoidFunction*)(&xrGetHandMeshFB_));
691 if (xrCreateHandTrackerEXT_) {
692 XrHandTrackerCreateInfoEXT createInfo{};
693 createInfo.type = XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT;
694 createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
695 createInfo.hand = XR_HAND_LEFT_EXT;
696 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::LeftHand])))
697 qWarning(
"Failed to create left hand tracker");
698 createInfo.hand = XR_HAND_RIGHT_EXT;
699 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::RightHand])))
700 qWarning(
"Failed to create right hand tracker");
702 if (xrGetHandMeshFB_) {
703 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
704 if (queryHandMesh(hand))
705 createHandModelData(hand);
712 XrHandTrackingMeshFB mesh {};
713 mesh.type = XR_TYPE_HAND_TRACKING_MESH_FB;
715 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
716 qWarning(
"Failed to query hand mesh info.");
720 mesh.jointCapacityInput = mesh.jointCountOutput;
721 mesh.vertexCapacityInput = mesh.vertexCountOutput;
722 mesh.indexCapacityInput = mesh.indexCountOutput;
723 m_handMeshData[hand].vertexPositions.resize(mesh.vertexCapacityInput);
724 m_handMeshData[hand].vertexNormals.resize(mesh.vertexCapacityInput);
725 m_handMeshData[hand].vertexUVs.resize(mesh.vertexCapacityInput);
726 m_handMeshData[hand].vertexBlendIndices.resize(mesh.vertexCapacityInput);
727 m_handMeshData[hand].vertexBlendWeights.resize(mesh.vertexCapacityInput);
728 m_handMeshData[hand].indices.resize(mesh.indexCapacityInput);
729 mesh.jointBindPoses = m_handMeshData[hand].jointBindPoses;
730 mesh.jointParents = m_handMeshData[hand].jointParents;
731 mesh.jointRadii = m_handMeshData[hand].jointRadii;
732 mesh.vertexPositions = m_handMeshData[hand].vertexPositions.data();
733 mesh.vertexNormals = m_handMeshData[hand].vertexNormals.data();
734 mesh.vertexUVs = m_handMeshData[hand].vertexUVs.data();
735 mesh.vertexBlendIndices = m_handMeshData[hand].vertexBlendIndices.data();
736 mesh.vertexBlendWeights = m_handMeshData[hand].vertexBlendWeights.data();
737 mesh.indices = m_handMeshData[hand].indices.data();
739 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
740 qWarning(
"Failed to get hand mesh data.");
749 m_handInputActionDefs = {
750 { QQuick3DXrInputAction::Button1Pressed,
"b1_pressed",
"Button 1 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
751 { QQuick3DXrInputAction::Button1Touched,
"b1_touched",
"Button 1 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
752 { QQuick3DXrInputAction::Button2Pressed,
"b2_pressed",
"Button 2 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
753 { QQuick3DXrInputAction::Button2Touched,
"b2_touched",
"Button 2 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
754 { QQuick3DXrInputAction::ButtonMenuPressed,
"bmenu_pressed",
"Button Menu Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
755 { QQuick3DXrInputAction::ButtonMenuTouched,
"bmenu_touched",
"Button Menu Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
756 { QQuick3DXrInputAction::ButtonSystemPressed,
"bsystem_pressed",
"Button System Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
757 { QQuick3DXrInputAction::ButtonSystemTouched,
"bsystem_touched",
"Button System Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
758 { QQuick3DXrInputAction::SqueezeValue,
"squeeze_value",
"Squeeze Value", XR_ACTION_TYPE_FLOAT_INPUT },
759 { QQuick3DXrInputAction::SqueezeForce,
"squeeze_force",
"Squeeze Force", XR_ACTION_TYPE_FLOAT_INPUT },
760 { QQuick3DXrInputAction::SqueezePressed,
"squeeze_pressed",
"Squeeze Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
761 { QQuick3DXrInputAction::TriggerValue,
"trigger_value",
"Trigger Value", XR_ACTION_TYPE_FLOAT_INPUT },
762 { QQuick3DXrInputAction::TriggerPressed,
"trigger_pressed",
"Trigger Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
763 { QQuick3DXrInputAction::TriggerTouched,
"trigger_touched",
"Trigger Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
764 { QQuick3DXrInputAction::ThumbstickX,
"thumbstick_x",
"Thumbstick X", XR_ACTION_TYPE_FLOAT_INPUT },
765 { QQuick3DXrInputAction::ThumbstickY,
"thumbstick_y",
"Thumbstick Y", XR_ACTION_TYPE_FLOAT_INPUT },
766 { QQuick3DXrInputAction::ThumbstickPressed,
"thumbstick_pressed",
"Thumbstick Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
767 { QQuick3DXrInputAction::ThumbstickTouched,
"thumbstick_touched",
"Thumbstick Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
768 { QQuick3DXrInputAction::ThumbrestTouched,
"thumbrest_touched",
"Thumbrest Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
769 { QQuick3DXrInputAction::TrackpadX,
"trackpad_x",
"Trackpad X", XR_ACTION_TYPE_FLOAT_INPUT },
770 { QQuick3DXrInputAction::TrackpadY,
"trackpad_y",
"Trackpad Y", XR_ACTION_TYPE_FLOAT_INPUT },
771 { QQuick3DXrInputAction::TrackpadForce,
"trackpad_force",
"Trackpad Force", XR_ACTION_TYPE_FLOAT_INPUT },
772 { QQuick3DXrInputAction::TrackpadTouched,
"trackpad_touched",
"Trackpad Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
773 { QQuick3DXrInputAction::TrackpadPressed,
"trackpad_pressed",
"Trackpad Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT }
778 XrActionSetCreateInfo actionSetInfo{};
779 actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
780 strcpy(actionSetInfo.actionSetName,
"gameplay");
781 strcpy(actionSetInfo.localizedActionSetName,
"Gameplay");
782 actionSetInfo.priority = 0;
783 if (!checkXrResult(xrCreateActionSet(m_instance, &actionSetInfo, &m_actionSet)))
784 qWarning(
"Failed to create gameplay action set");
788 setPath(m_handSubactionPath[0],
"/user/hand/left");
789 setPath(m_handSubactionPath[1],
"/user/hand/right");
791 for (
const auto &def : m_handInputActionDefs) {
792 createAction(def.type,
797 m_inputActions[def.id]);
800 createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT,
805 m_handActions.hapticAction);
806 createAction(XR_ACTION_TYPE_POSE_INPUT,
811 m_handActions.gripPoseAction);
812 createAction(XR_ACTION_TYPE_POSE_INPUT,
817 m_handActions.aimPoseAction);
823 for (
auto &action : m_inputActions) {
825 xrDestroyAction(action);
828 xrDestroyAction(m_handActions.gripPoseAction);
829 xrDestroyAction(m_handActions.aimPoseAction);
830 xrDestroyAction(m_handActions.hapticAction);
832 xrDestroyActionSet(m_actionSet);
837 return OpenXRHelpers::checkXrResult(result, m_instance);
842 if (!checkXrResult(xrStringToPath(m_instance, pathString.constData(), &path)))
843 qWarning(
"xrStringToPath failed");
848 const char *localizedName,
850 XrPath *subactionPath,
853 XrActionCreateInfo actionInfo{};
854 actionInfo.type = XR_TYPE_ACTION_CREATE_INFO;
855 actionInfo.actionType = type;
856 strcpy(actionInfo.actionName, name);
857 strcpy(actionInfo.localizedActionName, localizedName);
858 actionInfo.countSubactionPaths = quint32(numSubactions);
859 actionInfo.subactionPaths = subactionPath;
860 if (!checkXrResult(xrCreateAction(m_actionSet, &actionInfo, &action)))
861 qCDebug(lcQuick3DXr) <<
"xrCreateAction failed. Name:" << name <<
"localizedName:" << localizedName;
866 getInfo.action = action;
867 XrActionStateBoolean boolValue{};
868 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
869 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
870 if (boolValue.isActive == XR_TRUE)
871 setter(
bool(boolValue.currentState));
873 qWarning(
"Failed to get action state: bool");
879 getInfo.action = action;
880 XrActionStateFloat floatValue{};
881 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
882 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
883 if (floatValue.isActive == XR_TRUE)
884 setter(
float(floatValue.currentState));
886 qWarning(
"Failed to get action state: float");
892 if (poseSpace == HandPoseSpace::GripPose)
893 return m_handGripSpace[hand];
895 return m_handAimSpace[hand];
900 return m_handInputState[hand]->isActive();
905 return m_handInputState[hand]->isHandTrackingActive();
910 for (
auto *controller : std::as_const(m_controllers)) {
911 if (QtQuick3DXr::handForController(controller->controller()) == hand && QtQuick3DXr::pose_cast(controller->poseSpace()) == poseSpace) {
912 controller->setPosition(position);
913 controller->setRotation(rotation);
920 QSSG_ASSERT(hand < 2, hand = Hand::LeftHand);
921 QQuick3DXrActionMapper::handleInput(QQuick3DXrInputAction::Action(id),
static_cast<QQuick3DXrInputAction::Controller>(hand), shortName, value);
926 return m_handInputState[Hand::LeftHand];
931 return m_handInputState[Hand::RightHand];
936 QMatrix4x4 transform = QMatrix4x4{rotation.toRotationMatrix()};
938 transform(0, 3) += position[0];
939 transform(1, 3) += position[1];
940 transform(2, 3) += position[2];
947 QQuick3DGeometry *geometry = m_handGeometryData[hand].geometry;
951 model->setGeometry(geometry);
953 QQuick3DSkin *skin =
new QQuick3DSkin(model);
954 auto jointListProp = skin->joints();
955 QList<QMatrix4x4> inverseBindPoses;
956 inverseBindPoses.reserve(XR_HAND_JOINT_COUNT_EXT);
958 const auto &handMeshData = m_handMeshData[hand];
960 for (
int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) {
961 const auto &pose = handMeshData.jointBindPoses[i];
962 const QVector3D pos = OpenXRHelpers::toQVector(pose.position);
963 const QQuaternion rot = OpenXRHelpers::toQQuaternion(pose.orientation);
964 inverseBindPoses.append(transformMatrix(pos, rot).inverted());
965 QQuick3DNode *joint =
new QQuick3DNode(model);
966 joint->setPosition(pos);
967 joint->setRotation(rot);
968 jointListProp.append(&jointListProp, joint);
970 skin->setInverseBindPoses(inverseBindPoses);
971 model->setSkin(skin);
976 QSSG_ASSERT(model !=
nullptr,
return);
978 if (model->geometry() !=
nullptr || model->skin() !=
nullptr) {
979 qWarning() <<
"Hand model already has geometry or skin set.";
983 auto hand = model->hand();
984 if (hand == QQuick3DXrHandModel::LeftHand)
985 setupHandModelInternal(model, Hand::LeftHand);
986 else if (hand == QQuick3DXrHandModel::RightHand)
987 setupHandModelInternal(model, Hand::RightHand);
989 qWarning() <<
"No matching hand tracker input found for hand model.";
995 m_poseUsageDirty =
true;
996 if (controller->controller() == QQuick3DXrController::ControllerNone) {
997 m_controllers.remove(controller);
1001 m_controllers.insert(controller);
1006 m_poseUsageDirty = m_controllers.remove(controller);
1011 QSSG_ASSERT(uint(hand) < 2 && uint(poseSpace) < 2,
return false);
1012 if (m_poseUsageDirty) {
1013 std::fill_n(&m_poseInUse[0][0], 4,
false);
1014 for (
const auto *controller : std::as_const(m_controllers)) {
1015 m_poseInUse[uint(controller->controller())][uint(controller->poseSpace())] =
true;
1017 m_poseUsageDirty =
false;
1019 return m_poseInUse[uint(hand)][uint(poseSpace)];
1024 const auto &handMeshData = m_handMeshData[hand];
1026 auto &geometry = m_handGeometryData[hand].geometry;
1028 geometry = createHandMeshGeometry(handMeshData);