6#include "../qquick3dxrinputmanager_p.h"
8#include "openxr/qopenxrhelpers_p.h"
16#include <private/qquick3djoint_p.h>
18#include <QtGui/qquaternion.h>
22Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
27 m_handInputState[Hand::LeftHand] =
new QQuick3DXrHandInput(
this);
28 m_handInputState[Hand::RightHand] =
new QQuick3DXrHandInput(
this);
34 delete m_handInputState[Hand::LeftHand];
35 delete m_handInputState[Hand::RightHand];
37 m_handInputState[Hand::LeftHand] =
nullptr;
38 m_handInputState[Hand::RightHand] =
nullptr;
43 QXRHandComponentPath res;
44 setPath(res.paths[Hand::LeftHand],
"/user/hand/left/" + path);
45 setPath(res.paths[Hand::RightHand],
"/user/hand/right/" + path);
53 setPath(res, path.toByteArray());
59 QQuick3DGeometry *geometry =
new QQuick3DGeometry();
60 geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
63 const qsizetype expectedLength = handMeshData.vertexPositions.size();
64 bool hasPositions = !handMeshData.vertexPositions.isEmpty();
65 bool hasNormals = handMeshData.vertexNormals.size() >= expectedLength;
66 bool hasUV0s = handMeshData.vertexUVs.size() >= expectedLength;
67 bool hasJoints = handMeshData.vertexBlendIndices.size() >= expectedLength;
68 bool hasWeights = handMeshData.vertexBlendWeights.size() >= expectedLength;
69 bool hasIndexes = !handMeshData.indices.isEmpty();
73 geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
74 offset += 3 *
sizeof(
float);
78 geometry->addAttribute(QQuick3DGeometry::Attribute::NormalSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
79 offset += 3 *
sizeof(
float);
83 geometry->addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
84 offset += 2 *
sizeof(
float);
88 geometry->addAttribute(QQuick3DGeometry::Attribute::JointSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::I32Type);
89 offset += 4 *
sizeof(qint32);
93 geometry->addAttribute(QQuick3DGeometry::Attribute::WeightSemantic, offset, QQuick3DGeometry::Attribute::ComponentType::F32Type);
94 offset += 4 *
sizeof(
float);
98 geometry->addAttribute(QQuick3DGeometry::Attribute::IndexSemantic, 0, QQuick3DGeometry::Attribute::ComponentType::U16Type);
101 const int stride = offset;
102 const qsizetype bufferSize = expectedLength * stride;
103 geometry->setStride(stride);
105 QByteArray vertexBuffer;
106 vertexBuffer.reserve(bufferSize);
111 auto appendFloat = [&vertexBuffer](
float f) {
112 vertexBuffer.append(
reinterpret_cast<
const char *>(&f),
sizeof(
float));
114 auto appendInt = [&vertexBuffer](qint32 i) {
115 vertexBuffer.append(
reinterpret_cast<
const char *>(&i),
sizeof(qint32));
118 for (qsizetype i = 0; i < expectedLength; ++i) {
121 const QVector3D position = OpenXRHelpers::toQVector(handMeshData.vertexPositions[i]);
122 appendFloat(position.x());
123 appendFloat(position.y());
124 appendFloat(position.z());
125 minBounds.setX(qMin(minBounds.x(), position.x()));
126 maxBounds.setX(qMax(maxBounds.x(), position.x()));
127 minBounds.setY(qMin(minBounds.y(), position.y()));
128 maxBounds.setY(qMax(maxBounds.y(), position.y()));
129 minBounds.setZ(qMin(minBounds.z(), position.z()));
130 maxBounds.setZ(qMax(maxBounds.z(), position.z()));
133 const auto &normal = handMeshData.vertexNormals[i];
134 appendFloat(normal.x);
135 appendFloat(normal.y);
136 appendFloat(normal.z);
140 const auto &uv0 = handMeshData.vertexUVs[i];
146 const auto &joint = handMeshData.vertexBlendIndices[i];
154 const auto &weight = handMeshData.vertexBlendWeights[i];
155 appendFloat(weight.x);
156 appendFloat(weight.y);
157 appendFloat(weight.z);
158 appendFloat(weight.w);
162 geometry->setBounds(minBounds, maxBounds);
163 geometry->setVertexData(vertexBuffer);
167 const qsizetype indexLength = handMeshData.indices.size();
168 QByteArray indexBuffer;
169 indexBuffer.reserve(indexLength *
sizeof(int16_t));
170 for (qsizetype i = 0; i < indexLength; ++i) {
171 const auto &index = handMeshData.indices[i];
172 indexBuffer.append(
reinterpret_cast<
const char *>(&index),
sizeof(int16_t));
174 geometry->setIndexData(indexBuffer);
183 QList<ActionPaths> GripAimHapticSupported = {
184 ActionPaths::leftGripPose,
185 ActionPaths::leftAimPose,
186 ActionPaths::leftHaptic,
187 ActionPaths::rightGripPose,
188 ActionPaths::rightAimPose,
189 ActionPaths::rightHaptic
193 QList<InputMapping> oculusTouchInputMapping = {
194 InputMapping{QQuick3DXrInputAction::Button1Pressed, InputNames::XClick, LeftHandSubPath},
195 InputMapping{QQuick3DXrInputAction::Button1Pressed, InputNames::AClick, RightHandSubPath},
196 InputMapping{QQuick3DXrInputAction::Button2Pressed, InputNames::YClick, LeftHandSubPath},
197 InputMapping{QQuick3DXrInputAction::Button2Pressed, InputNames::BClick, RightHandSubPath},
198 InputMapping{QQuick3DXrInputAction::Button1Touched, InputNames::XTouch, LeftHandSubPath},
199 InputMapping{QQuick3DXrInputAction::Button1Touched, InputNames::ATouch, RightHandSubPath},
200 InputMapping{QQuick3DXrInputAction::Button2Touched, InputNames::YTouch, LeftHandSubPath},
201 InputMapping{QQuick3DXrInputAction::Button2Touched, InputNames::BTouch, RightHandSubPath},
202 InputMapping{QQuick3DXrInputAction::ButtonMenuPressed, InputNames::MenuClick, LeftHandSubPath},
203 InputMapping{QQuick3DXrInputAction::ButtonSystemPressed, InputNames::SystemClick, RightHandSubPath},
204 InputMapping{QQuick3DXrInputAction::SqueezeValue, InputNames::SqueezeValue, BothHandsSubPath},
205 InputMapping{QQuick3DXrInputAction::TriggerValue, InputNames::TriggerValue, BothHandsSubPath},
206 InputMapping{QQuick3DXrInputAction::TriggerTouched, InputNames::TriggerTouch, BothHandsSubPath},
207 InputMapping{QQuick3DXrInputAction::ThumbstickX, InputNames::ThumbstickX, BothHandsSubPath},
208 InputMapping{QQuick3DXrInputAction::ThumbstickY, InputNames::ThumbstickY, BothHandsSubPath},
209 InputMapping{QQuick3DXrInputAction::ThumbstickPressed, InputNames::ThumbstickClick, BothHandsSubPath},
210 InputMapping{QQuick3DXrInputAction::ThumbstickTouched, InputNames::ThumbstickTouch, BothHandsSubPath},
211 InputMapping{QQuick3DXrInputAction::ThumbrestTouched, InputNames::ThumbrestTouch, BothHandsSubPath},
214 ControllerBindings oculusTouch{
216 "/interaction_profiles/oculus/touch_controller",
217 oculusTouchInputMapping,
218 GripAimHapticSupported
220 controllerBindingsList->append(oculusTouch);
223 QList<InputMapping> viveControllerInputMapping {
224 InputMapping{QQuick3DXrInputAction::ButtonMenuPressed, InputNames::MenuClick, BothHandsSubPath},
225 InputMapping{QQuick3DXrInputAction::ButtonSystemPressed, InputNames::SystemClick, BothHandsSubPath},
226 InputMapping{QQuick3DXrInputAction::SqueezePressed, InputNames::SqueezeClick, BothHandsSubPath},
227 InputMapping{QQuick3DXrInputAction::TriggerValue, InputNames::TriggerValue, BothHandsSubPath},
228 InputMapping{QQuick3DXrInputAction::TriggerPressed, InputNames::TriggerClick, BothHandsSubPath},
229 InputMapping{QQuick3DXrInputAction::TrackpadX, InputNames::TrackpadX, BothHandsSubPath},
230 InputMapping{QQuick3DXrInputAction::TrackpadY, InputNames::TrackpadY, BothHandsSubPath},
231 InputMapping{QQuick3DXrInputAction::TrackpadPressed, InputNames::TrackpadClick, BothHandsSubPath},
232 InputMapping{QQuick3DXrInputAction::TrackpadTouched, InputNames::TrackpadTouch, BothHandsSubPath},
235 ControllerBindings viveController {
237 "/interaction_profiles/htc/vive_controller",
238 viveControllerInputMapping,
239 GripAimHapticSupported
241 controllerBindingsList->append(viveController);
245 QList<InputMapping> microsoftHandInteractionExtensionInputMapping {
246 InputMapping{QQuick3DXrInputAction::SqueezeValue, InputNames::SqueezeValue, BothHandsSubPath},
249 QList<ActionPaths> microsoftHandInteractionExtensionActionPaths = {
250 ActionPaths::leftGripPose,
251 ActionPaths::leftAimPose,
252 ActionPaths::rightGripPose,
253 ActionPaths::rightAimPose
256 ControllerBindings microsoftHandInteractionExtension {
257 "microsoftHandInteractionExtension",
258 "/interaction_profiles/microsoft/hand_interaction",
259 microsoftHandInteractionExtensionInputMapping,
260 microsoftHandInteractionExtensionActionPaths
262 controllerBindingsList->append(microsoftHandInteractionExtension);
266 QList<InputMapping> microsoftMRMInputMapping {};
267 QList<ActionPaths> microsoftMRMActionPaths;
269 ControllerBindings microsoftMRM{
271 "/interaction_profiles/microsoft/motion_controller",
272 microsoftMRMInputMapping,
273 microsoftMRMActionPaths
275 controllerBindingsList->append(microsoftMRM);
278 QList<InputMapping> valveIndexInputMapping {};
279 QList<ActionPaths> valveIndexActionPaths;
281 ControllerBindings valveIndex{
283 "/interaction_profiles/valve/index_controller",
284 valveIndexInputMapping,
285 valveIndexActionPaths
287 controllerBindingsList->append(valveIndex);
291void QQuick3DXrInputManagerPrivate::setUpBindings(QList<ControllerBindings>* controllerBindingsList, QMap<InputNames, QXRHandComponentPath>* handComponentPaths)
293 QMap<ActionPaths, XrActionSuggestedBinding> actionPaths;
300 setPath(leftGripPose,
"/user/hand/left/input/grip/pose");
301 setPath(leftAimPose,
"/user/hand/left/input/aim/pose");
302 setPath(leftHaptic,
"/user/hand/left/output/haptic");
304 actionPaths.insert(ActionPaths::leftGripPose, {m_handActions.gripPoseAction, leftGripPose});
305 actionPaths.insert(ActionPaths::leftAimPose, {m_handActions.aimPoseAction, leftAimPose});
306 actionPaths.insert(ActionPaths::leftHaptic, {m_handActions.hapticAction, leftHaptic});
309 XrPath rightGripPose;
313 setPath(rightGripPose,
"/user/hand/right/input/grip/pose");
314 setPath(rightAimPose,
"/user/hand/right/input/aim/pose");
315 setPath(rightHaptic,
"/user/hand/right/output/haptic");
317 actionPaths.insert(ActionPaths::rightGripPose, {m_handActions.gripPoseAction, rightGripPose});
318 actionPaths.insert(ActionPaths::rightAimPose, {m_handActions.aimPoseAction, rightAimPose});
319 actionPaths.insert(ActionPaths::rightHaptic, {m_handActions.hapticAction, rightHaptic});
321 for (
int i = 0; i < controllerBindingsList->size(); i++) {
322 ControllerBindings controllerBindings = controllerBindingsList->at(i);
324 if (controllerBindings.profileMappingDefs.size() == 0 || controllerBindings.supportedActionPaths.size() == 0)
328 setPath(profilePath, controllerBindings.profilePath);
330 std::vector<XrActionSuggestedBinding> bindings {};
332 for (
const auto& path : controllerBindings.supportedActionPaths) {
333 bindings.push_back(actionPaths.value(path));
336 for (
const auto &[actionId, path, selector] : controllerBindings.profileMappingDefs) {
337 if (selector & LeftHandSubPath)
338 bindings.push_back({m_inputActions[actionId], handComponentPaths->value(path).paths[Hand::LeftHand]});
339 if (selector & RightHandSubPath)
340 bindings.push_back({m_inputActions[actionId], handComponentPaths->value(path).paths[Hand::RightHand]});
343 XrInteractionProfileSuggestedBinding suggestedBindings{};
344 suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
345 suggestedBindings.interactionProfile = profilePath;
346 suggestedBindings.suggestedBindings = bindings.data();
347 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
348 if (!checkXrResult(xrSuggestInteractionProfileBindings(m_instance, &suggestedBindings)))
349 qWarning() <<
"Failed to get suggested interaction profile bindings for " << controllerBindings.profileName;
356 qWarning() <<
"QQuick3DXrInputManager: Trying to initialize an already initialized session";
360 m_instance = instance;
367 QMap<InputNames, QXRHandComponentPath> handComponentPaths;
369 handComponentPaths.insert(InputNames::AClick, makeHandInputPaths(
"input/a/click"));
370 handComponentPaths.insert(InputNames::BClick, makeHandInputPaths(
"input/b/click"));
371 handComponentPaths.insert(InputNames::ATouch, makeHandInputPaths(
"input/a/touch"));
372 handComponentPaths.insert(InputNames::BTouch, makeHandInputPaths(
"input/b/touch"));
374 handComponentPaths.insert(InputNames::XClick, makeHandInputPaths(
"input/x/click"));
375 handComponentPaths.insert(InputNames::YClick, makeHandInputPaths(
"input/y/click"));
376 handComponentPaths.insert(InputNames::XTouch, makeHandInputPaths(
"input/x/touch"));
377 handComponentPaths.insert(InputNames::YTouch, makeHandInputPaths(
"input/y/touch"));
379 handComponentPaths.insert(InputNames::MenuClick, makeHandInputPaths(
"input/menu/click"));
380 handComponentPaths.insert(InputNames::SystemClick, makeHandInputPaths(
"input/system/click"));
381 handComponentPaths.insert(InputNames::SystemTouch, makeHandInputPaths(
"input/system/touch"));
383 handComponentPaths.insert(InputNames::SqueezeValue, makeHandInputPaths(
"input/squeeze/value"));
384 handComponentPaths.insert(InputNames::SqueezeForce, makeHandInputPaths(
"input/squeeze/force"));
385 handComponentPaths.insert(InputNames::SqueezeClick, makeHandInputPaths(
"input/squeeze/click"));
387 handComponentPaths.insert(InputNames::TriggerValue, makeHandInputPaths(
"input/trigger/value"));
388 handComponentPaths.insert(InputNames::TriggerTouch, makeHandInputPaths(
"input/trigger/touch"));
389 handComponentPaths.insert(InputNames::TriggerClick, makeHandInputPaths(
"input/trigger/click"));
391 handComponentPaths.insert(InputNames::ThumbstickX, makeHandInputPaths(
"input/thumbstick/x"));
392 handComponentPaths.insert(InputNames::ThumbstickY, makeHandInputPaths(
"input/thumbstick/y"));
393 handComponentPaths.insert(InputNames::ThumbstickClick, makeHandInputPaths(
"input/thumbstick/click"));
394 handComponentPaths.insert(InputNames::ThumbstickTouch, makeHandInputPaths(
"input/thumbstick/touch"));
395 handComponentPaths.insert(InputNames::ThumbrestTouch, makeHandInputPaths(
"input/thumbrest/touch"));
397 handComponentPaths.insert(InputNames::TrackpadX, makeHandInputPaths(
"input/trackpad/x"));
398 handComponentPaths.insert(InputNames::TrackpadY, makeHandInputPaths(
"input/trackpad/y"));
399 handComponentPaths.insert(InputNames::TrackpadForce, makeHandInputPaths(
"input/trackpad/force"));
400 handComponentPaths.insert(InputNames::TrackpadClick, makeHandInputPaths(
"input/trackpad/click"));
401 handComponentPaths.insert(InputNames::TrackpadTouch, makeHandInputPaths(
"input/trackpad/touch"));
404 QList<ControllerBindings> controllerBindingsList;
405 loadBindings(&controllerBindingsList);
406 setUpBindings(&controllerBindingsList, &handComponentPaths);
410 XrActionSpaceCreateInfo actionSpaceInfo{};
411 actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
412 actionSpaceInfo.action = m_handActions.gripPoseAction;
413 actionSpaceInfo.poseInActionSpace.orientation.w = 1.0f;
415 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
416 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[0])))
417 qWarning(
"Failed to create action space for handGripSpace[0]");
418 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
419 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handGripSpace[1])))
420 qWarning(
"Failed to create action space for handGripSpace[1]");
422 actionSpaceInfo.action = m_handActions.aimPoseAction;
423 actionSpaceInfo.subactionPath = m_handSubactionPath[0];
424 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[0])))
425 qWarning(
"Failed to create action space for handAimSpace[0]");
426 actionSpaceInfo.subactionPath = m_handSubactionPath[1];
427 if (!checkXrResult(xrCreateActionSpace(m_session, &actionSpaceInfo, &m_handAimSpace[1])))
428 qWarning(
"Failed to create action space for handAimSpace[1]");
432 XrSessionActionSetsAttachInfo attachInfo{};
433 attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
434 attachInfo.countActionSets = 1;
435 attachInfo.actionSets = &m_actionSet;
436 if (!checkXrResult(xrAttachSessionActionSets(m_session, &attachInfo)))
437 qWarning(
"Failed to attach action sets to session");
439 m_initialized =
true;
447 m_initialized =
false;
449 xrDestroySpace(m_handGripSpace[0]);
450 xrDestroySpace(m_handGripSpace[1]);
451 xrDestroySpace(m_handAimSpace[0]);
452 xrDestroySpace(m_handAimSpace[1]);
456 if (xrDestroyHandTrackerEXT_) {
457 xrDestroyHandTrackerEXT_(handTracker[Hand::LeftHand]);
458 xrDestroyHandTrackerEXT_(handTracker[Hand::RightHand]);
461 m_instance = {XR_NULL_HANDLE};
462 m_session = {XR_NULL_HANDLE};
467 QSSG_ASSERT(inputManager !=
nullptr,
return nullptr);
468 return inputManager->d_func();
477 const XrActiveActionSet activeActionSet{m_actionSet, XR_NULL_PATH};
478 XrActionsSyncInfo syncInfo{};
479 syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO;
480 syncInfo.countActiveActionSets = 1;
481 syncInfo.activeActionSets = &activeActionSet;
482 XrResult result = xrSyncActions(m_session, &syncInfo);
483 if (!(result == XR_SUCCESS ||
484 result == XR_SESSION_LOSS_PENDING ||
485 result == XR_SESSION_NOT_FOCUSED))
487 if (!checkXrResult(result)) {
488 qWarning(
"xrSyncActions failed");
494 XrActionStateGetInfo getInfo{};
495 getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO;
496 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
498 getInfo.subactionPath = m_handSubactionPath[hand];
499 auto &inputState = m_handInputState[hand];
501 for (
const auto &def : m_handInputActionDefs) {
502 getInfo.action = m_inputActions[def.id];
504 case XR_ACTION_TYPE_BOOLEAN_INPUT: {
505 XrActionStateBoolean boolValue{};
506 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
507 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
508 if (boolValue.isActive && boolValue.changedSinceLastSync) {
510 setInputValue(hand, def.id, def.shortName,
float(boolValue.currentState));
513 qWarning(
"Failed to get action state for bool hand input");
517 case XR_ACTION_TYPE_FLOAT_INPUT: {
518 XrActionStateFloat floatValue{};
519 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
520 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
521 if (floatValue.isActive && floatValue.changedSinceLastSync) {
523 setInputValue(hand, def.id, def.shortName,
float(floatValue.currentState));
526 qWarning(
"Failed to get action state for float hand input");
530 case XR_ACTION_TYPE_VECTOR2F_INPUT:
531 case XR_ACTION_TYPE_POSE_INPUT:
532 case XR_ACTION_TYPE_VIBRATION_OUTPUT:
533 case XR_ACTION_TYPE_MAX_ENUM:
539 getInfo.action = m_handActions.gripPoseAction;
540 XrActionStatePose poseState{};
541 poseState.type = XR_TYPE_ACTION_STATE_POSE;
542 if (checkXrResult(xrGetActionStatePose(m_session, &getInfo, &poseState)))
543 inputState->setIsActive(poseState.isActive);
545 qWarning(
"Failed to get action state pose");
551 const QList<QPointer<QQuick3DXrHapticFeedback>> hapticOutputData = QQuick3DXrActionMapper::getHapticEffects(
static_cast<QQuick3DXrInputAction::Controller>(hand));
553 for (
auto &hapticFeedback : hapticOutputData) {
554 const bool triggered = hapticFeedback->testAndClear();
556 if (
auto *hapticEffect = qobject_cast<QQuick3DXrSimpleHapticEffect*>(hapticFeedback->hapticEffect())) {
557 XrHapticVibration vibration {XR_TYPE_HAPTIC_VIBRATION,
nullptr, 0, 0, 0};
558 vibration.amplitude = hapticEffect->amplitude();
559 vibration.duration = hapticEffect->duration() * 1000000;
560 vibration.frequency = hapticEffect->frequency();
562 XrHapticActionInfo hapticActionInfo {XR_TYPE_HAPTIC_ACTION_INFO,
nullptr, m_handActions.hapticAction, m_handSubactionPath[hand]};
564 if (!checkXrResult(xrApplyHapticFeedback(m_session, &hapticActionInfo, (
const XrHapticBaseHeader*)&vibration))) {
565 qWarning(
"Failed to trigger haptic feedback");
577 for (
auto poseSpace : {HandPoseSpace::AimPose, HandPoseSpace::GripPose}) {
578 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
579 if (!isPoseInUse(hand, poseSpace))
581 XrSpaceLocation spaceLocation{};
582 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
584 res = xrLocateSpace(handSpace(hand, poseSpace), appSpace, predictedDisplayTime, &spaceLocation);
588 m_validAimStateFromUpdatePoses[hand] = poseSpace == HandPoseSpace::AimPose
589 && XR_UNQUALIFIED_SUCCESS(res) && (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
590 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT);
592 if (XR_UNQUALIFIED_SUCCESS(res)) {
593 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
594 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
597 setPosePositionAndRotation(hand, poseSpace,
598 QVector3D(spaceLocation.pose.position.x,
599 spaceLocation.pose.position.y,
600 spaceLocation.pose.position.z) * 100.0f,
601 QQuaternion(spaceLocation.pose.orientation.w,
602 spaceLocation.pose.orientation.x,
603 spaceLocation.pose.orientation.y,
604 spaceLocation.pose.orientation.z));
609 if (isHandActive(hand)) {
610 const char* handName[] = {
"left",
"right"};
611 qCDebug(lcQuick3DXr,
"Unable to locate %s hand action space in app space: %d", handName[hand], res);
620 if (xrLocateHandJointsEXT_) {
622 XrHandTrackingAimStateFB aimState[2] = {{}, {}};
623 XrHandJointVelocitiesEXT velocities[2]{{}, {}};
624 XrHandJointLocationsEXT locations[2]{{}, {}};
625 XrHandJointsLocateInfoEXT locateInfo[2] = {{}, {}};
627 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
628 if (handTracker[hand] == XR_NULL_HANDLE)
631 aimState[hand].type = XR_TYPE_HAND_TRACKING_AIM_STATE_FB;
633 velocities[hand].type = XR_TYPE_HAND_JOINT_VELOCITIES_EXT;
634 velocities[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
635 velocities[hand].jointVelocities = jointVelocities[hand];
636 velocities[hand].next = aimExtensionEnabled ? &aimState[hand] :
nullptr;
638 locations[hand].type = XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
639 locations[hand].next = &velocities[hand];
640 locations[hand].jointCount = XR_HAND_JOINT_COUNT_EXT;
641 locations[hand].jointLocations = jointLocations[hand];
643 locateInfo[hand].type = XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT;
644 locateInfo[hand].baseSpace = appSpace;
645 locateInfo[hand].time = predictedDisplayTime;
646 if (!checkXrResult(xrLocateHandJointsEXT_(handTracker[hand], &locateInfo[hand], &locations[hand])))
647 qWarning(
"Failed to locate hand joints for hand tracker");
650 jp.reserve(XR_HAND_JOINT_COUNT_EXT);
651 QList<QQuaternion> jr;
652 jr.reserve(XR_HAND_JOINT_COUNT_EXT);
653 for (uint i = 0; i < locations[hand].jointCount; ++i) {
654 auto &pose = jointLocations[hand][i].pose;
655 jp.append(OpenXRHelpers::toQVector(pose.position));
656 jr.append(OpenXRHelpers::toQQuaternion(pose.orientation));
658 m_handInputState[hand]->setJointPositionsAndRotations(jp, jr);
659 m_handInputState[hand]->setIsHandTrackingActive(locations[hand].isActive);
662 if (aimExtensionEnabled) {
664 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
665 const uint state = aimState[hand].status;
666 const uint oldState = m_aimStateFlags[hand];
667 auto updateState = [&](
const char *name, QQuick3DXrInputAction::Action id, uint flag) {
668 if ((state & flag) != (oldState & flag))
669 setInputValue(hand, id, name,
float(!!(state & flag)));
672 updateState(
"index_pinch", QQuick3DXrInputAction::IndexFingerPinch, XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB);
673 updateState(
"middle_pinch", QQuick3DXrInputAction::MiddleFingerPinch, XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB);
674 updateState(
"ring_pinch", QQuick3DXrInputAction::RingFingerPinch, XR_HAND_TRACKING_AIM_RING_PINCHING_BIT_FB);
675 updateState(
"little_pinch", QQuick3DXrInputAction::LittleFingerPinch, XR_HAND_TRACKING_AIM_LITTLE_PINCHING_BIT_FB);
676 updateState(
"hand_tracking_menu_press", QQuick3DXrInputAction::HandTrackingMenuPress, XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB);
677 m_aimStateFlags[hand] = state;
681 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
682 if (isPoseInUse(hand, HandPoseSpace::AimPose) && !m_validAimStateFromUpdatePoses[hand]) {
683 if ((aimState[hand].status & XR_HAND_TRACKING_AIM_VALID_BIT_FB)) {
684 setPosePositionAndRotation(hand, HandPoseSpace::AimPose,
685 QVector3D(aimState[hand].aimPose.position.x,
686 aimState[hand].aimPose.position.y,
687 aimState[hand].aimPose.position.z) * 100.0f,
688 QQuaternion(aimState[hand].aimPose.orientation.w,
689 aimState[hand].aimPose.orientation.x,
690 aimState[hand].aimPose.orientation.y,
691 aimState[hand].aimPose.orientation.z));
692 m_handInputState[hand]->setIsActive(
true);
704 "xrCreateHandTrackerEXT",
705 (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_));
708 "xrDestroyHandTrackerEXT",
709 (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_));
712 "xrLocateHandJointsEXT",
713 (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_));
717 (PFN_xrVoidFunction*)(&xrGetHandMeshFB_));
719 if (xrCreateHandTrackerEXT_) {
720 XrHandTrackerCreateInfoEXT createInfo{};
721 createInfo.type = XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT;
722 createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
723 createInfo.hand = XR_HAND_LEFT_EXT;
724 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::LeftHand])))
725 qWarning(
"Failed to create left hand tracker");
726 createInfo.hand = XR_HAND_RIGHT_EXT;
727 if (!checkXrResult(xrCreateHandTrackerEXT_(m_session, &createInfo, &handTracker[QtQuick3DXr::RightHand])))
728 qWarning(
"Failed to create right hand tracker");
730 if (xrGetHandMeshFB_) {
731 for (
auto hand : {Hand::LeftHand, Hand::RightHand}) {
732 if (queryHandMesh(hand))
733 createHandModelData(hand);
740 XrHandTrackingMeshFB mesh {};
741 mesh.type = XR_TYPE_HAND_TRACKING_MESH_FB;
743 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
744 qWarning(
"Failed to query hand mesh info.");
748 mesh.jointCapacityInput = mesh.jointCountOutput;
749 mesh.vertexCapacityInput = mesh.vertexCountOutput;
750 mesh.indexCapacityInput = mesh.indexCountOutput;
751 m_handMeshData[hand].vertexPositions.resize(mesh.vertexCapacityInput);
752 m_handMeshData[hand].vertexNormals.resize(mesh.vertexCapacityInput);
753 m_handMeshData[hand].vertexUVs.resize(mesh.vertexCapacityInput);
754 m_handMeshData[hand].vertexBlendIndices.resize(mesh.vertexCapacityInput);
755 m_handMeshData[hand].vertexBlendWeights.resize(mesh.vertexCapacityInput);
756 m_handMeshData[hand].indices.resize(mesh.indexCapacityInput);
757 mesh.jointBindPoses = m_handMeshData[hand].jointBindPoses;
758 mesh.jointParents = m_handMeshData[hand].jointParents;
759 mesh.jointRadii = m_handMeshData[hand].jointRadii;
760 mesh.vertexPositions = m_handMeshData[hand].vertexPositions.data();
761 mesh.vertexNormals = m_handMeshData[hand].vertexNormals.data();
762 mesh.vertexUVs = m_handMeshData[hand].vertexUVs.data();
763 mesh.vertexBlendIndices = m_handMeshData[hand].vertexBlendIndices.data();
764 mesh.vertexBlendWeights = m_handMeshData[hand].vertexBlendWeights.data();
765 mesh.indices = m_handMeshData[hand].indices.data();
767 if (!checkXrResult(xrGetHandMeshFB_(handTracker[hand], &mesh))) {
768 qWarning(
"Failed to get hand mesh data.");
777 m_handInputActionDefs = {
778 { QQuick3DXrInputAction::Button1Pressed,
"b1_pressed",
"Button 1 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
779 { QQuick3DXrInputAction::Button1Touched,
"b1_touched",
"Button 1 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
780 { QQuick3DXrInputAction::Button2Pressed,
"b2_pressed",
"Button 2 Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
781 { QQuick3DXrInputAction::Button2Touched,
"b2_touched",
"Button 2 Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
782 { QQuick3DXrInputAction::ButtonMenuPressed,
"bmenu_pressed",
"Button Menu Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
783 { QQuick3DXrInputAction::ButtonMenuTouched,
"bmenu_touched",
"Button Menu Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
784 { QQuick3DXrInputAction::ButtonSystemPressed,
"bsystem_pressed",
"Button System Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
785 { QQuick3DXrInputAction::ButtonSystemTouched,
"bsystem_touched",
"Button System Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
786 { QQuick3DXrInputAction::SqueezeValue,
"squeeze_value",
"Squeeze Value", XR_ACTION_TYPE_FLOAT_INPUT },
787 { QQuick3DXrInputAction::SqueezeForce,
"squeeze_force",
"Squeeze Force", XR_ACTION_TYPE_FLOAT_INPUT },
788 { QQuick3DXrInputAction::SqueezePressed,
"squeeze_pressed",
"Squeeze Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
789 { QQuick3DXrInputAction::TriggerValue,
"trigger_value",
"Trigger Value", XR_ACTION_TYPE_FLOAT_INPUT },
790 { QQuick3DXrInputAction::TriggerPressed,
"trigger_pressed",
"Trigger Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
791 { QQuick3DXrInputAction::TriggerTouched,
"trigger_touched",
"Trigger Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
792 { QQuick3DXrInputAction::ThumbstickX,
"thumbstick_x",
"Thumbstick X", XR_ACTION_TYPE_FLOAT_INPUT },
793 { QQuick3DXrInputAction::ThumbstickY,
"thumbstick_y",
"Thumbstick Y", XR_ACTION_TYPE_FLOAT_INPUT },
794 { QQuick3DXrInputAction::ThumbstickPressed,
"thumbstick_pressed",
"Thumbstick Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT },
795 { QQuick3DXrInputAction::ThumbstickTouched,
"thumbstick_touched",
"Thumbstick Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
796 { QQuick3DXrInputAction::ThumbrestTouched,
"thumbrest_touched",
"Thumbrest Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
797 { QQuick3DXrInputAction::TrackpadX,
"trackpad_x",
"Trackpad X", XR_ACTION_TYPE_FLOAT_INPUT },
798 { QQuick3DXrInputAction::TrackpadY,
"trackpad_y",
"Trackpad Y", XR_ACTION_TYPE_FLOAT_INPUT },
799 { QQuick3DXrInputAction::TrackpadForce,
"trackpad_force",
"Trackpad Force", XR_ACTION_TYPE_FLOAT_INPUT },
800 { QQuick3DXrInputAction::TrackpadTouched,
"trackpad_touched",
"Trackpad Touched", XR_ACTION_TYPE_BOOLEAN_INPUT },
801 { QQuick3DXrInputAction::TrackpadPressed,
"trackpad_pressed",
"Trackpad Pressed", XR_ACTION_TYPE_BOOLEAN_INPUT }
806 XrActionSetCreateInfo actionSetInfo{};
807 actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
808 strcpy(actionSetInfo.actionSetName,
"gameplay");
809 strcpy(actionSetInfo.localizedActionSetName,
"Gameplay");
810 actionSetInfo.priority = 0;
811 if (!checkXrResult(xrCreateActionSet(m_instance, &actionSetInfo, &m_actionSet)))
812 qWarning(
"Failed to create gameplay action set");
816 setPath(m_handSubactionPath[0],
"/user/hand/left");
817 setPath(m_handSubactionPath[1],
"/user/hand/right");
819 for (
const auto &def : m_handInputActionDefs) {
820 createAction(def.type,
825 m_inputActions[def.id]);
828 createAction(XR_ACTION_TYPE_VIBRATION_OUTPUT,
833 m_handActions.hapticAction);
834 createAction(XR_ACTION_TYPE_POSE_INPUT,
839 m_handActions.gripPoseAction);
840 createAction(XR_ACTION_TYPE_POSE_INPUT,
845 m_handActions.aimPoseAction);
851 for (
auto &action : m_inputActions) {
853 xrDestroyAction(action);
856 xrDestroyAction(m_handActions.gripPoseAction);
857 xrDestroyAction(m_handActions.aimPoseAction);
858 xrDestroyAction(m_handActions.hapticAction);
860 xrDestroyActionSet(m_actionSet);
865 return OpenXRHelpers::checkXrResult(result, m_instance);
870 if (!checkXrResult(xrStringToPath(m_instance, pathString.constData(), &path)))
871 qWarning(
"xrStringToPath failed");
876 const char *localizedName,
878 XrPath *subactionPath,
881 XrActionCreateInfo actionInfo{};
882 actionInfo.type = XR_TYPE_ACTION_CREATE_INFO;
883 actionInfo.actionType = type;
884 strcpy(actionInfo.actionName, name);
885 strcpy(actionInfo.localizedActionName, localizedName);
886 actionInfo.countSubactionPaths = quint32(numSubactions);
887 actionInfo.subactionPaths = subactionPath;
888 if (!checkXrResult(xrCreateAction(m_actionSet, &actionInfo, &action)))
889 qCDebug(lcQuick3DXr) <<
"xrCreateAction failed. Name:" << name <<
"localizedName:" << localizedName;
894 getInfo.action = action;
895 XrActionStateBoolean boolValue{};
896 boolValue.type = XR_TYPE_ACTION_STATE_BOOLEAN;
897 if (checkXrResult(xrGetActionStateBoolean(m_session, &getInfo, &boolValue))) {
898 if (boolValue.isActive == XR_TRUE)
899 setter(
bool(boolValue.currentState));
901 qWarning(
"Failed to get action state: bool");
907 getInfo.action = action;
908 XrActionStateFloat floatValue{};
909 floatValue.type = XR_TYPE_ACTION_STATE_FLOAT;
910 if (checkXrResult(xrGetActionStateFloat(m_session, &getInfo, &floatValue))) {
911 if (floatValue.isActive == XR_TRUE)
912 setter(
float(floatValue.currentState));
914 qWarning(
"Failed to get action state: float");
920 if (poseSpace == HandPoseSpace::GripPose)
921 return m_handGripSpace[hand];
923 return m_handAimSpace[hand];
928 return m_handInputState[hand]->isActive();
933 return m_handInputState[hand]->isHandTrackingActive();
938 for (
auto *controller : std::as_const(m_controllers)) {
939 if (QtQuick3DXr::handForController(controller->controller()) == hand && QtQuick3DXr::pose_cast(controller->poseSpace()) == poseSpace) {
940 controller->setPosition(position);
941 controller->setRotation(rotation);
948 QSSG_ASSERT(hand < 2, hand = Hand::LeftHand);
949 QQuick3DXrActionMapper::handleInput(QQuick3DXrInputAction::Action(id),
static_cast<QQuick3DXrInputAction::Controller>(hand), shortName, value);
954 return m_handInputState[Hand::LeftHand];
959 return m_handInputState[Hand::RightHand];
964 QMatrix4x4 transform = QMatrix4x4{rotation.toRotationMatrix()};
966 transform(0, 3) += position[0];
967 transform(1, 3) += position[1];
968 transform(2, 3) += position[2];
975 QQuick3DGeometry *geometry = m_handGeometryData[hand].geometry;
979 model->setGeometry(geometry);
981 QQuick3DSkin *skin =
new QQuick3DSkin(model);
982 auto jointListProp = skin->joints();
983 QList<QMatrix4x4> inverseBindPoses;
984 inverseBindPoses.reserve(XR_HAND_JOINT_COUNT_EXT);
986 const auto &handMeshData = m_handMeshData[hand];
988 for (
int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) {
989 const auto &pose = handMeshData.jointBindPoses[i];
990 const QVector3D pos = OpenXRHelpers::toQVector(pose.position);
991 const QQuaternion rot = OpenXRHelpers::toQQuaternion(pose.orientation);
992 inverseBindPoses.append(transformMatrix(pos, rot).inverted());
993 QQuick3DNode *joint =
new QQuick3DNode(model);
994 joint->setPosition(pos);
995 joint->setRotation(rot);
996 jointListProp.append(&jointListProp, joint);
998 skin->setInverseBindPoses(inverseBindPoses);
999 model->setSkin(skin);
1004 QSSG_ASSERT(model !=
nullptr,
return);
1006 if (model->geometry() !=
nullptr || model->skin() !=
nullptr) {
1007 qWarning() <<
"Hand model already has geometry or skin set.";
1011 auto hand = model->hand();
1012 if (hand == QQuick3DXrHandModel::LeftHand)
1013 setupHandModelInternal(model, Hand::LeftHand);
1014 else if (hand == QQuick3DXrHandModel::RightHand)
1015 setupHandModelInternal(model, Hand::RightHand);
1017 qWarning() <<
"No matching hand tracker input found for hand model.";
1023 m_poseUsageDirty =
true;
1024 if (controller->controller() == QQuick3DXrController::ControllerNone) {
1025 m_controllers.remove(controller);
1029 m_controllers.insert(controller);
1034 m_poseUsageDirty = m_controllers.remove(controller);
1039 QSSG_ASSERT(uint(hand) < 2 && uint(poseSpace) < 2,
return false);
1040 if (m_poseUsageDirty) {
1041 std::fill_n(&m_poseInUse[0][0], 4,
false);
1042 for (
const auto *controller : std::as_const(m_controllers)) {
1043 m_poseInUse[uint(controller->controller())][uint(controller->poseSpace())] =
true;
1045 m_poseUsageDirty =
false;
1047 return m_poseInUse[uint(hand)][uint(poseSpace)];
1052 const auto &handMeshData = m_handMeshData[hand];
1054 auto &geometry = m_handGeometryData[hand].geometry;
1056 geometry = createHandMeshGeometry(handMeshData);
Combined button and popup list for selecting options.