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 QList<ActionPaths> GripAimHapticSupported = {
182 ActionPaths::leftGripPose,
183 ActionPaths::leftAimPose,
184 ActionPaths::leftHaptic,
185 ActionPaths::rightGripPose,
186 ActionPaths::rightAimPose,
187 ActionPaths::rightHaptic
191 QList<InputMapping> oculusTouchInputMapping = {
192 InputMapping{QQuick3DXrInputAction::Button1Pressed, InputNames::XClick, LeftHandSubPath},
193 InputMapping{QQuick3DXrInputAction::Button1Pressed, InputNames::AClick, RightHandSubPath},
194 InputMapping{QQuick3DXrInputAction::Button2Pressed, InputNames::YClick, LeftHandSubPath},
195 InputMapping{QQuick3DXrInputAction::Button2Pressed, InputNames::BClick, RightHandSubPath},
196 InputMapping{QQuick3DXrInputAction::Button1Touched, InputNames::XTouch, LeftHandSubPath},
197 InputMapping{QQuick3DXrInputAction::Button1Touched, InputNames::ATouch, RightHandSubPath},
198 InputMapping{QQuick3DXrInputAction::Button2Touched, InputNames::YTouch, LeftHandSubPath},
199 InputMapping{QQuick3DXrInputAction::Button2Touched, InputNames::BTouch, RightHandSubPath},
200 InputMapping{QQuick3DXrInputAction::ButtonMenuPressed, InputNames::MenuClick, LeftHandSubPath},
201 InputMapping{QQuick3DXrInputAction::ButtonSystemPressed, InputNames::SystemClick, RightHandSubPath},
202 InputMapping{QQuick3DXrInputAction::SqueezeValue, InputNames::SqueezeValue, BothHandsSubPath},
203 InputMapping{QQuick3DXrInputAction::TriggerValue, InputNames::TriggerValue, BothHandsSubPath},
204 InputMapping{QQuick3DXrInputAction::TriggerTouched, InputNames::TriggerTouch, BothHandsSubPath},
205 InputMapping{QQuick3DXrInputAction::ThumbstickX, InputNames::ThumbstickX, BothHandsSubPath},
206 InputMapping{QQuick3DXrInputAction::ThumbstickY, InputNames::ThumbstickY, BothHandsSubPath},
207 InputMapping{QQuick3DXrInputAction::ThumbstickPressed, InputNames::ThumbstickClick, BothHandsSubPath},
208 InputMapping{QQuick3DXrInputAction::ThumbstickTouched, InputNames::ThumbstickTouch, BothHandsSubPath},
209 InputMapping{QQuick3DXrInputAction::ThumbrestTouched, InputNames::ThumbrestTouch, BothHandsSubPath},
212 ControllerBindings oculusTouch{
214 "/interaction_profiles/oculus/touch_controller",
215 oculusTouchInputMapping,
216 GripAimHapticSupported
218 controllerBindingsList->append(oculusTouch);
221 QList<InputMapping> viveControllerInputMapping {
222 InputMapping{QQuick3DXrInputAction::ButtonMenuPressed, InputNames::MenuClick, BothHandsSubPath},
223 InputMapping{QQuick3DXrInputAction::ButtonSystemPressed, InputNames::SystemClick, BothHandsSubPath},
224 InputMapping{QQuick3DXrInputAction::SqueezePressed, InputNames::SqueezeClick, BothHandsSubPath},
225 InputMapping{QQuick3DXrInputAction::TriggerValue, InputNames::TriggerValue, BothHandsSubPath},
226 InputMapping{QQuick3DXrInputAction::TriggerPressed, InputNames::TriggerClick, BothHandsSubPath},
227 InputMapping{QQuick3DXrInputAction::TrackpadX, InputNames::TrackpadX, BothHandsSubPath},
228 InputMapping{QQuick3DXrInputAction::TrackpadY, InputNames::TrackpadY, BothHandsSubPath},
229 InputMapping{QQuick3DXrInputAction::TrackpadPressed, InputNames::TrackpadClick, BothHandsSubPath},
230 InputMapping{QQuick3DXrInputAction::TrackpadTouched, InputNames::TrackpadTouch, BothHandsSubPath},
233 ControllerBindings viveController {
235 "/interaction_profiles/htc/vive_controller",
236 viveControllerInputMapping,
237 GripAimHapticSupported
239 controllerBindingsList->append(viveController);
243 QList<InputMapping> microsoftHandInteractionExtensionInputMapping {
244 InputMapping{QQuick3DXrInputAction::SqueezeValue, InputNames::SqueezeValue, BothHandsSubPath},
247 QList<ActionPaths> microsoftHandInteractionExtensionActionPaths = {
248 ActionPaths::leftGripPose,
249 ActionPaths::leftAimPose,
250 ActionPaths::rightGripPose,
251 ActionPaths::rightAimPose
254 ControllerBindings microsoftHandInteractionExtension {
255 "microsoftHandInteractionExtension",
256 "/interaction_profiles/microsoft/hand_interaction",
257 microsoftHandInteractionExtensionInputMapping,
258 microsoftHandInteractionExtensionActionPaths
260 controllerBindingsList->append(microsoftHandInteractionExtension);
264 QList<InputMapping> microsoftMRMInputMapping {};
265 QList<ActionPaths> microsoftMRMActionPaths;
267 ControllerBindings microsoftMRM{
269 "/interaction_profiles/microsoft/motion_controller",
270 microsoftMRMInputMapping,
271 microsoftMRMActionPaths
273 controllerBindingsList->append(microsoftMRM);
276 QList<InputMapping> valveIndexInputMapping {};
277 QList<ActionPaths> valveIndexActionPaths;
279 ControllerBindings valveIndex{
281 "/interaction_profiles/valve/index_controller",
282 valveIndexInputMapping,
283 valveIndexActionPaths
285 controllerBindingsList->append(valveIndex);
289void QQuick3DXrInputManagerPrivate::setUpBindings(QList<ControllerBindings>* controllerBindingsList, QMap<InputNames, QXRHandComponentPath>* handComponentPaths)
291 QMap<ActionPaths, XrActionSuggestedBinding> actionPaths;
298 setPath(leftGripPose,
"/user/hand/left/input/grip/pose");
299 setPath(leftAimPose,
"/user/hand/left/input/aim/pose");
300 setPath(leftHaptic,
"/user/hand/left/output/haptic");
302 actionPaths.insert(ActionPaths::leftGripPose, {m_handActions.gripPoseAction, leftGripPose});
303 actionPaths.insert(ActionPaths::leftAimPose, {m_handActions.aimPoseAction, leftAimPose});
304 actionPaths.insert(ActionPaths::leftHaptic, {m_handActions.hapticAction, leftHaptic});
307 XrPath rightGripPose;
311 setPath(rightGripPose,
"/user/hand/right/input/grip/pose");
312 setPath(rightAimPose,
"/user/hand/right/input/aim/pose");
313 setPath(rightHaptic,
"/user/hand/right/output/haptic");
315 actionPaths.insert(ActionPaths::rightGripPose, {m_handActions.gripPoseAction, rightGripPose});
316 actionPaths.insert(ActionPaths::rightAimPose, {m_handActions.aimPoseAction, rightAimPose});
317 actionPaths.insert(ActionPaths::rightHaptic, {m_handActions.hapticAction, rightHaptic});
319 for (
int i = 0; i < controllerBindingsList->size(); i++) {
320 ControllerBindings controllerBindings = controllerBindingsList->at(i);
322 if (controllerBindings.profileMappingDefs.size() == 0 || controllerBindings.supportedActionPaths.size() == 0)
326 setPath(profilePath, controllerBindings.profilePath);
328 std::vector<XrActionSuggestedBinding> bindings {};
330 for (
const auto& path : controllerBindings.supportedActionPaths) {
331 bindings.push_back(actionPaths.value(path));
334 for (
const auto &[actionId, path, selector] : controllerBindings.profileMappingDefs) {
335 if (selector & LeftHandSubPath)
336 bindings.push_back({m_inputActions[actionId], handComponentPaths->value(path).paths[Hand::LeftHand]});
337 if (selector & RightHandSubPath)
338 bindings.push_back({m_inputActions[actionId], handComponentPaths->value(path).paths[Hand::RightHand]});
341 XrInteractionProfileSuggestedBinding suggestedBindings{};
342 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
343 suggestedBindings.interactionProfile = profilePath;
344 suggestedBindings.suggestedBindings = bindings.data();
345 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
346 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
347 qWarning() <<
"Failed to get suggested interaction profile bindings for " << controllerBindings.profileName;
354 qWarning() <<
"QQuick3DXrInputManager: Trying to initialize an already initialized session";
358 m_instance = instance;
365 QMap<InputNames, QXRHandComponentPath> handComponentPaths;
367 handComponentPaths.insert(InputNames::AClick, makeHandInputPaths(
"input/a/click"));
368 handComponentPaths.insert(InputNames::BClick, makeHandInputPaths(
"input/b/click"));
369 handComponentPaths.insert(InputNames::ATouch, makeHandInputPaths(
"input/a/touch"));
370 handComponentPaths.insert(InputNames::BTouch, makeHandInputPaths(
"input/b/touch"));
372 handComponentPaths.insert(InputNames::XClick, makeHandInputPaths(
"input/x/click"));
373 handComponentPaths.insert(InputNames::YClick, makeHandInputPaths(
"input/y/click"));
374 handComponentPaths.insert(InputNames::XTouch, makeHandInputPaths(
"input/x/touch"));
375 handComponentPaths.insert(InputNames::YTouch, makeHandInputPaths(
"input/y/touch"));
377 handComponentPaths.insert(InputNames::MenuClick, makeHandInputPaths(
"input/menu/click"));
378 handComponentPaths.insert(InputNames::SystemClick, makeHandInputPaths(
"input/system/click"));
379 handComponentPaths.insert(InputNames::SystemTouch, makeHandInputPaths(
"input/system/touch"));
381 handComponentPaths.insert(InputNames::SqueezeValue, makeHandInputPaths(
"input/squeeze/value"));
382 handComponentPaths.insert(InputNames::SqueezeForce, makeHandInputPaths(
"input/squeeze/force"));
383 handComponentPaths.insert(InputNames::SqueezeClick, makeHandInputPaths(
"input/squeeze/click"));
385 handComponentPaths.insert(InputNames::TriggerValue, makeHandInputPaths(
"input/trigger/value"));
386 handComponentPaths.insert(InputNames::TriggerTouch, makeHandInputPaths(
"input/trigger/touch"));
387 handComponentPaths.insert(InputNames::TriggerClick, makeHandInputPaths(
"input/trigger/click"));
389 handComponentPaths.insert(InputNames::ThumbstickX, makeHandInputPaths(
"input/thumbstick/x"));
390 handComponentPaths.insert(InputNames::ThumbstickY, makeHandInputPaths(
"input/thumbstick/y"));
391 handComponentPaths.insert(InputNames::ThumbstickClick, makeHandInputPaths(
"input/thumbstick/click"));
392 handComponentPaths.insert(InputNames::ThumbstickTouch, makeHandInputPaths(
"input/thumbstick/touch"));
393 handComponentPaths.insert(InputNames::ThumbrestTouch, makeHandInputPaths(
"input/thumbrest/touch"));
395 handComponentPaths.insert(InputNames::TrackpadX, makeHandInputPaths(
"input/trackpad/x"));
396 handComponentPaths.insert(InputNames::TrackpadY, makeHandInputPaths(
"input/trackpad/y"));
397 handComponentPaths.insert(InputNames::TrackpadForce, makeHandInputPaths(
"input/trackpad/force"));
398 handComponentPaths.insert(InputNames::TrackpadClick, makeHandInputPaths(
"input/trackpad/click"));
399 handComponentPaths.insert(InputNames::TrackpadTouch, makeHandInputPaths(
"input/trackpad/touch"));
402 QList<ControllerBindings> controllerBindingsList;
403 loadBindings(&controllerBindingsList);
404 setUpBindings(&controllerBindingsList, &handComponentPaths);
408 XrActionSpaceCreateInfo actionSpaceInfo{};
409 actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
410 actionSpaceInfo.action = m_handActions.gripPoseAction;
411 actionSpaceInfo.poseInActionSpace.orientation.w = 1.0f;
413 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
414 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[0])))
415 qWarning(
"Failed to create action space for handGripSpace[0]");
416 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
417 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[1])))
418 qWarning(
"Failed to create action space for handGripSpace[1]");
420 actionSpaceInfo.action = m_handActions.aimPoseAction;
421 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
422 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[0])))
423 qWarning(
"Failed to create action space for handAimSpace[0]");
424 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
425 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[1])))
426 qWarning(
"Failed to create action space for handAimSpace[1]");
430 XrSessionActionSetsAttachInfo attachInfo{};
431 attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
432 attachInfo.countActionSets = 1;
433 attachInfo.actionSets = &m_actionSet;
434 if (!checkXrResult(xrAttachSessionActionSets(m_session, &attachInfo)))
435 qWarning(
"Failed to attach action sets to session");
437 m_initialized =
true;
445 m_initialized =
false;
447 xrDestroySpace(m_handGripSpace[0]);
448 xrDestroySpace(m_handGripSpace[1]);
449 xrDestroySpace(m_handAimSpace[0]);
450 xrDestroySpace(m_handAimSpace[1]);
454 if (xrDestroyHandTrackerEXT_) {
455 xrDestroyHandTrackerEXT_(handTracker[Hand::LeftHand]);
456 xrDestroyHandTrackerEXT_(handTracker[Hand::RightHand]);
459 m_instance = {XR_NULL_HANDLE};
460 m_session = {XR_NULL_HANDLE};
465 QSSG_ASSERT(inputManager !=
nullptr,
return nullptr);
466 return inputManager->d_func();
475 const XrActiveActionSet activeActionSet{m_actionSet, XR_NULL_PATH};
476 XrActionsSyncInfo syncInfo{};
477 syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO;
478 syncInfo.countActiveActionSets = 1;
479 syncInfo.activeActionSets = &activeActionSet;
480 XrResult result = xrSyncActions(m_session, &syncInfo);
481 if (!(result == XR_SUCCESS ||
482 result == XR_SESSION_LOSS_PENDING ||
483 result == XR_SESSION_NOT_FOCUSED))
485 if (!checkXrResult(result)) {
486 qWarning(
"xrSyncActions failed");
492 XrActionStateGetInfo getInfo{};
493 getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
494 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
496 getInfo.subactionPath = m_handSubactionPath[hand];
497 auto &inputState = m_handInputState[hand];
499 for (
const auto &def : m_handInputActionDefs) {
500 getInfo.action = m_inputActions[def.id];
502 case XR_ACTION_TYPE_BOOLEAN_INPUT: {
503 XrActionStateBoolean boolValue{};
504 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
505 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
506 if (boolValue.isActive && boolValue.changedSinceLastSync) {
508 setInputValue(hand, def.id, def.shortName,
float(boolValue.currentState));
511 qWarning(
"Failed to get action state for bool hand input");
515 case XR_ACTION_TYPE_FLOAT_INPUT: {
516 XrActionStateFloat floatValue{};
517 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
518 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
519 if (floatValue.isActive && floatValue.changedSinceLastSync) {
521 setInputValue(hand, def.id, def.shortName,
float(floatValue.currentState));
524 qWarning(
"Failed to get action state for float hand input");
528 case XR_ACTION_TYPE_VECTOR2F_INPUT:
529 case XR_ACTION_TYPE_POSE_INPUT:
530 case XR_ACTION_TYPE_VIBRATION_OUTPUT:
531 case XR_ACTION_TYPE_MAX_ENUM:
537 getInfo.action = m_handActions.gripPoseAction;
538 XrActionStatePose poseState{};
539 poseState.type = XR_TYPE_ACTION_STATE_POSE;
540 if (checkXrResult(xrGetActionStatePose(m_session, &getInfo, &poseState)))
541 inputState->setIsActive(poseState.isActive);
543 qWarning(
"Failed to get action state pose");
549 const QList<QPointer<QQuick3DXrHapticFeedback>> hapticOutputData = QQuick3DXrActionMapper::getHapticEffects(
static_cast<QQuick3DXrInputAction::Controller>(hand));
551 for (
auto &hapticFeedback : hapticOutputData) {
552 const bool triggered = hapticFeedback->testAndClear();
554 if (
auto *hapticEffect = qobject_cast<QQuick3DXrSimpleHapticEffect*>(hapticFeedback->hapticEffect())) {
555 XrHapticVibration vibration {XR_TYPE_HAPTIC_VIBRATION,
nullptr, 0, 0, 0};
556 vibration.amplitude = hapticEffect->amplitude();
557 vibration.duration = hapticEffect->duration() * 1000000;
558 vibration.frequency = hapticEffect->frequency();
560 XrHapticActionInfo hapticActionInfo {XR_TYPE_HAPTIC_ACTION_INFO,
nullptr, m_handActions.hapticAction, m_handSubactionPath[hand]};
562 if (!checkXrResult(xrApplyHapticFeedback(m_session, &hapticActionInfo, (
const XrHapticBaseHeader*)&vibration))) {
563 qWarning(
"Failed to trigger haptic feedback");
575 for (
auto poseSpace : {HandPoseSpace::AimPose, HandPoseSpace::GripPose}) {
576 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
577 if (!isPoseInUse(hand, poseSpace))
579 XrSpaceLocation spaceLocation{};
580 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
582 res = xrLocateSpace(handSpace(hand, poseSpace), appSpace, predictedDisplayTime, &spaceLocation);
586 m_validAimStateFromUpdatePoses[hand] = poseSpace == HandPoseSpace::AimPose
587 && XR_UNQUALIFIED_SUCCESS(res) && (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
588 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT);
590 if (XR_UNQUALIFIED_SUCCESS(res)) {
591 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
592 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
595 setPosePositionAndRotation(hand, poseSpace,
596 QVector3D(spaceLocation.pose.position.x,
597 spaceLocation.pose.position.y,
598 spaceLocation.pose.position.z) * 100.0f,
599 QQuaternion(spaceLocation.pose.orientation.w,
600 spaceLocation.pose.orientation.x,
601 spaceLocation.pose.orientation.y,
602 spaceLocation.pose.orientation.z));
607 if (isHandActive(hand)) {
608 const char* handName[] = {
"left",
"right"};
609 qCDebug(lcQuick3DXr,
"Unable to locate %s hand action space in app space: %d", handName[hand], res);
618 if (xrLocateHandJointsEXT_) {
620 XrHandTrackingAimStateFB aimState[2] = {{}, {}};
621 XrHandJointVelocitiesEXT velocities[2]{{}, {}};
622 XrHandJointLocationsEXT locations[2]{{}, {}};
623 XrHandJointsLocateInfoEXT locateInfo[2] = {{}, {}};
625 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
626 if (handTracker[hand] == XR_NULL_HANDLE)
629 aimState[hand].type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
631 velocities[hand].type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
632 velocities[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
633 velocities[hand].jointVelocities = jointVelocities[hand];
634 velocities[hand].next = aimExtensionEnabled ? &aimState[hand] :
nullptr;
636 locations[hand].type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
637 locations[hand].next = &velocities[hand];
638 locations[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
639 locations[hand].jointLocations = jointLocations[hand];
641 locateInfo[hand].type = XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT;
642 locateInfo[hand].baseSpace = appSpace;
643 locateInfo[hand].time = predictedDisplayTime;
644 if (!checkXrResult(xrLocateHandJointsEXT_(handTracker[hand], &locateInfo[hand], &locations[hand])))
645 qWarning(
"Failed to locate hand joints for hand tracker");
648 jp.reserve(XR_HAND_JOINT_COUNT_EXT);
649 QList<QQuaternion> jr;
650 jr.reserve(XR_HAND_JOINT_COUNT_EXT);
651 for (uint i = 0; i < locations[hand].jointCount; ++i) {
652 auto &pose = jointLocations[hand][i].pose;
653 jp.append(OpenXRHelpers::toQVector(pose.position));
654 jr.append(OpenXRHelpers::toQQuaternion(pose.orientation));
656 m_handInputState[hand]->setJointPositionsAndRotations(jp, jr);
657 m_handInputState[hand]->setIsHandTrackingActive(locations[hand].isActive);
660 if (aimExtensionEnabled) {
662 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
663 const uint state = aimState[hand].status;
664 const uint oldState = m_aimStateFlags[hand];
665 auto updateState = [&](
const char *name, QQuick3DXrInputAction::Action id, uint flag) {
666 if ((state & flag) != (oldState & flag))
667 setInputValue(hand, id, name,
float(!!(state & flag)));
670 updateState(
"index_pinch", QQuick3DXrInputAction::IndexFingerPinch, XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB);
671 updateState(
"middle_pinch", QQuick3DXrInputAction::MiddleFingerPinch, XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB);
672 updateState(
"ring_pinch", QQuick3DXrInputAction::RingFingerPinch, XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB);
673 updateState(
"little_pinch", QQuick3DXrInputAction::LittleFingerPinch, XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB);
674 updateState(
"hand_tracking_menu_press", QQuick3DXrInputAction::HandTrackingMenuPress, XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB);
675 m_aimStateFlags[hand] = state;
679 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
680 if (isPoseInUse(hand, HandPoseSpace::AimPose) && !m_validAimStateFromUpdatePoses[hand]) {
681 if ((aimState[hand].status & XR_HAND_TRACKING_AIM_VALID_BIT_FB)) {
682 setPosePositionAndRotation(hand, HandPoseSpace::AimPose,
683 QVector3D(aimState[hand].aimPose.position.x,
684 aimState[hand].aimPose.position.y,
685 aimState[hand].aimPose.position.z) * 100.0f,
686 QQuaternion(aimState[hand].aimPose.orientation.w,
687 aimState[hand].aimPose.orientation.x,
688 aimState[hand].aimPose.orientation.y,
689 aimState[hand].aimPose.orientation.z));
690 m_handInputState[hand]->setIsActive(
true);
700 OpenXRHelpers::resolveXrFunction(
702 "xrCreateHandTrackerEXT",
703 (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_));
704 OpenXRHelpers::resolveXrFunction(
706 "xrDestroyHandTrackerEXT",
707 (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_));
708 OpenXRHelpers::resolveXrFunction(
710 "xrLocateHandJointsEXT",
711 (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_));
712 OpenXRHelpers::resolveXrFunction(
715 (PFN_xrVoidFunction*)(&xrGetHandMeshFB_));
717 if (xrCreateHandTrackerEXT_) {
718 XrHandTrackerCreateInfoEXT createInfo{};
719 createInfo.type = XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT;
720 createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
721 createInfo.hand = XR_HAND_LEFT_EXT;
722 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::LeftHand])))
723 qWarning(
"Failed to create left hand tracker");
724 createInfo.hand = XR_HAND_RIGHT_EXT;
725 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::RightHand])))
726 qWarning(
"Failed to create right hand tracker");
728 if (xrGetHandMeshFB_) {
729 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
730 if (queryHandMesh(hand))
731 createHandModelData(hand);
738 XrHandTrackingMeshFB mesh {};
739 mesh.type = XR_TYPE_HAND_TRACKING_MESH_FB;
741 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
742 qWarning(
"Failed to query hand mesh info.");
746 mesh.jointCapacityInput = mesh.jointCountOutput;
747 mesh.vertexCapacityInput = mesh.vertexCountOutput;
748 mesh.indexCapacityInput = mesh.indexCountOutput;
749 m_handMeshData[hand].vertexPositions.resize(mesh.vertexCapacityInput);
750 m_handMeshData[hand].vertexNormals.resize(mesh.vertexCapacityInput);
751 m_handMeshData[hand].vertexUVs.resize(mesh.vertexCapacityInput);
752 m_handMeshData[hand].vertexBlendIndices.resize(mesh.vertexCapacityInput);
753 m_handMeshData[hand].vertexBlendWeights.resize(mesh.vertexCapacityInput);
754 m_handMeshData[hand].indices.resize(mesh.indexCapacityInput);
755 mesh.jointBindPoses = m_handMeshData[hand].jointBindPoses;
756 mesh.jointParents = m_handMeshData[hand].jointParents;
757 mesh.jointRadii = m_handMeshData[hand].jointRadii;
758 mesh.vertexPositions = m_handMeshData[hand].vertexPositions.data();
759 mesh.vertexNormals = m_handMeshData[hand].vertexNormals.data();
760 mesh.vertexUVs = m_handMeshData[hand].vertexUVs.data();
761 mesh.vertexBlendIndices = m_handMeshData[hand].vertexBlendIndices.data();
762 mesh.vertexBlendWeights = m_handMeshData[hand].vertexBlendWeights.data();
763 mesh.indices = m_handMeshData[hand].indices.data();
765 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
766 qWarning(
"Failed to get hand mesh data.");
775 m_handInputActionDefs = {
776 { QQuick3DXrInputAction::Button1Pressed,
"b1_pressed",
"Button 1 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
777 { QQuick3DXrInputAction::Button1Touched,
"b1_touched",
"Button 1 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
778 { QQuick3DXrInputAction::Button2Pressed,
"b2_pressed",
"Button 2 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
779 { QQuick3DXrInputAction::Button2Touched,
"b2_touched",
"Button 2 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
780 { QQuick3DXrInputAction::ButtonMenuPressed,
"bmenu_pressed",
"Button Menu Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
781 { QQuick3DXrInputAction::ButtonMenuTouched,
"bmenu_touched",
"Button Menu Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
782 { QQuick3DXrInputAction::ButtonSystemPressed,
"bsystem_pressed",
"Button System Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
783 { QQuick3DXrInputAction::ButtonSystemTouched,
"bsystem_touched",
"Button System Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
784 { QQuick3DXrInputAction::SqueezeValue,
"squeeze_value",
"Squeeze Value", XR_ACTION_TYPE_FLOAT_INPUT },
785 { QQuick3DXrInputAction::SqueezeForce,
"squeeze_force",
"Squeeze Force", XR_ACTION_TYPE_FLOAT_INPUT },
786 { QQuick3DXrInputAction::SqueezePressed,
"squeeze_pressed",
"Squeeze Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
787 { QQuick3DXrInputAction::TriggerValue,
"trigger_value",
"Trigger Value", XR_ACTION_TYPE_FLOAT_INPUT },
788 { QQuick3DXrInputAction::TriggerPressed,
"trigger_pressed",
"Trigger Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
789 { QQuick3DXrInputAction::TriggerTouched,
"trigger_touched",
"Trigger Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
790 { QQuick3DXrInputAction::ThumbstickX,
"thumbstick_x",
"Thumbstick X", XR_ACTION_TYPE_FLOAT_INPUT },
791 { QQuick3DXrInputAction::ThumbstickY,
"thumbstick_y",
"Thumbstick Y", XR_ACTION_TYPE_FLOAT_INPUT },
792 { QQuick3DXrInputAction::ThumbstickPressed,
"thumbstick_pressed",
"Thumbstick Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
793 { QQuick3DXrInputAction::ThumbstickTouched,
"thumbstick_touched",
"Thumbstick Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
794 { QQuick3DXrInputAction::ThumbrestTouched,
"thumbrest_touched",
"Thumbrest Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
795 { QQuick3DXrInputAction::TrackpadX,
"trackpad_x",
"Trackpad X", XR_ACTION_TYPE_FLOAT_INPUT },
796 { QQuick3DXrInputAction::TrackpadY,
"trackpad_y",
"Trackpad Y", XR_ACTION_TYPE_FLOAT_INPUT },
797 { QQuick3DXrInputAction::TrackpadForce,
"trackpad_force",
"Trackpad Force", XR_ACTION_TYPE_FLOAT_INPUT },
798 { QQuick3DXrInputAction::TrackpadTouched,
"trackpad_touched",
"Trackpad Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
799 { QQuick3DXrInputAction::TrackpadPressed,
"trackpad_pressed",
"Trackpad Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT }
804 XrActionSetCreateInfo actionSetInfo{};
805 actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
806 strcpy(actionSetInfo.actionSetName,
"gameplay");
807 strcpy(actionSetInfo.localizedActionSetName,
"Gameplay");
808 actionSetInfo.priority = 0;
809 if (!checkXrResult(xrCreateActionSet(m_instance, &actionSetInfo, &m_actionSet)))
810 qWarning(
"Failed to create gameplay action set");
814 setPath(m_handSubactionPath[0],
"/user/hand/left");
815 setPath(m_handSubactionPath[1],
"/user/hand/right");
817 for (
const auto &def : m_handInputActionDefs) {
818 createAction(def.type,
823 m_inputActions[def.id]);
826 createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT,
831 m_handActions.hapticAction);
832 createAction(XR_ACTION_TYPE_POSE_INPUT,
837 m_handActions.gripPoseAction);
838 createAction(XR_ACTION_TYPE_POSE_INPUT,
843 m_handActions.aimPoseAction);
849 for (
auto &action : m_inputActions) {
851 xrDestroyAction(action);
854 xrDestroyAction(m_handActions.gripPoseAction);
855 xrDestroyAction(m_handActions.aimPoseAction);
856 xrDestroyAction(m_handActions.hapticAction);
858 xrDestroyActionSet(m_actionSet);
863 return OpenXRHelpers::checkXrResult(result, m_instance);
868 if (!checkXrResult(xrStringToPath(m_instance, pathString.constData(), &path)))
869 qWarning(
"xrStringToPath failed");
874 const char *localizedName,
876 XrPath *subactionPath,
879 XrActionCreateInfo actionInfo{};
880 actionInfo.type = XR_TYPE_ACTION_CREATE_INFO;
881 actionInfo.actionType = type;
882 strcpy(actionInfo.actionName, name);
883 strcpy(actionInfo.localizedActionName, localizedName);
884 actionInfo.countSubactionPaths = quint32(numSubactions);
885 actionInfo.subactionPaths = subactionPath;
886 if (!checkXrResult(xrCreateAction(m_actionSet, &actionInfo, &action)))
887 qCDebug(lcQuick3DXr) <<
"xrCreateAction failed. Name:" << name <<
"localizedName:" << localizedName;
892 getInfo.action = action;
893 XrActionStateBoolean boolValue{};
894 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
895 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
896 if (boolValue.isActive == XR_TRUE)
897 setter(
bool(boolValue.currentState));
899 qWarning(
"Failed to get action state: bool");
905 getInfo.action = action;
906 XrActionStateFloat floatValue{};
907 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
908 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
909 if (floatValue.isActive == XR_TRUE)
910 setter(
float(floatValue.currentState));
912 qWarning(
"Failed to get action state: float");
918 if (poseSpace == HandPoseSpace::GripPose)
919 return m_handGripSpace[hand];
921 return m_handAimSpace[hand];
926 return m_handInputState[hand]->isActive();
931 return m_handInputState[hand]->isHandTrackingActive();
936 for (
auto *controller : std::as_const(m_controllers)) {
937 if (QtQuick3DXr::handForController(controller->controller()) == hand && QtQuick3DXr::pose_cast(controller->poseSpace()) == poseSpace) {
938 controller->setPosition(position);
939 controller->setRotation(rotation);
946 QSSG_ASSERT(hand < 2, hand = Hand::LeftHand);
947 QQuick3DXrActionMapper::handleInput(QQuick3DXrInputAction::Action(id),
static_cast<QQuick3DXrInputAction::Controller>(hand), shortName, value);
952 return m_handInputState[Hand::LeftHand];
957 return m_handInputState[Hand::RightHand];
962 QMatrix4x4 transform = QMatrix4x4{rotation.toRotationMatrix()};
964 transform(0, 3) += position[0];
965 transform(1, 3) += position[1];
966 transform(2, 3) += position[2];
973 QQuick3DGeometry *geometry = m_handGeometryData[hand].geometry;
977 model->setGeometry(geometry);
979 QQuick3DSkin *skin =
new QQuick3DSkin(model);
980 auto jointListProp = skin->joints();
981 QList<QMatrix4x4> inverseBindPoses;
982 inverseBindPoses.reserve(XR_HAND_JOINT_COUNT_EXT);
984 const auto &handMeshData = m_handMeshData[hand];
986 for (
int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) {
987 const auto &pose = handMeshData.jointBindPoses[i];
988 const QVector3D pos = OpenXRHelpers::toQVector(pose.position);
989 const QQuaternion rot = OpenXRHelpers::toQQuaternion(pose.orientation);
990 inverseBindPoses.append(transformMatrix(pos, rot).inverted());
991 QQuick3DNode *joint =
new QQuick3DNode(model);
992 joint->setPosition(pos);
993 joint->setRotation(rot);
994 jointListProp.append(&jointListProp, joint);
996 skin->setInverseBindPoses(inverseBindPoses);
997 model->setSkin(skin);
1002 QSSG_ASSERT(model !=
nullptr,
return);
1004 if (model->geometry() !=
nullptr || model->skin() !=
nullptr) {
1005 qWarning() <<
"Hand model already has geometry or skin set.";
1009 auto hand = model->hand();
1010 if (hand == QQuick3DXrHandModel::LeftHand)
1011 setupHandModelInternal(model, Hand::LeftHand);
1012 else if (hand == QQuick3DXrHandModel::RightHand)
1013 setupHandModelInternal(model, Hand::RightHand);
1015 qWarning() <<
"No matching hand tracker input found for hand model.";
1021 m_poseUsageDirty =
true;
1022 if (controller->controller() == QQuick3DXrController::ControllerNone) {
1023 m_controllers.remove(controller);
1027 m_controllers.insert(controller);
1032 m_poseUsageDirty = m_controllers.remove(controller);
1037 QSSG_ASSERT(uint(hand) < 2 && uint(poseSpace) < 2,
return false);
1038 if (m_poseUsageDirty) {
1039 std::fill_n(&m_poseInUse[0][0], 4,
false);
1040 for (
const auto *controller : std::as_const(m_controllers)) {
1041 m_poseInUse[uint(controller->controller())][uint(controller->poseSpace())] =
true;
1043 m_poseUsageDirty =
false;
1045 return m_poseInUse[uint(hand)][uint(poseSpace)];
1050 const auto &handMeshData = m_handMeshData[hand];
1052 auto &geometry = m_handGeometryData[hand].geometry;
1054 geometry = createHandMeshGeometry(handMeshData);
Combined button and popup list for selecting options.