7#include "../qquick3dxrinputmanager_p.h"
8#include "../qquick3dxractionmapper_p.h"
9#include "../qquick3dxrcontroller_p.h"
11#include <QtQuick3DUtils/private/qssgassert_p.h>
12#include <QtQuick3DUtils/private/qssgutils_p.h>
14#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
15#include <QtQuick3D/private/qquick3dviewport_p.h>
19Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
24 m_handInputState[Hand::LeftHand] =
new QQuick3DXrHandInput(&manager);
25 m_handInputState[Hand::RightHand] =
new QQuick3DXrHandInput(&manager);
36 QQuick3DXrActionMapper::handleInput(QQuick3DXrInputAction::Action(id),
static_cast<QQuick3DXrInputAction::Controller>(hand), shortName, value);
42 QSSG_ASSERT_X(!m_initialized,
"Handtracking is already initialized!",
return);
44 m_isHandTrackingSupported = ar_hand_tracking_provider_is_supported();
45 if (m_isHandTrackingSupported) {
46 ar_hand_tracking_configuration_t handTrackingConfiguration = ar_hand_tracking_configuration_create();
47 m_handTrackingProvider = ar_hand_tracking_provider_create(handTrackingConfiguration);
48 ar_data_providers_add_data_provider(dataProviders, m_handTrackingProvider);
50 qCWarning(lcQuick3DXr,
"Handtracking is not supported on this device!");
53 qCDebug(lcQuick3DXr) << Q_FUNC_INFO <<
", Handtracking supported: " << m_isHandTrackingSupported;
58 if (m_isHandTrackingSupported) {
59 m_handAnchors[Hand::LeftHand] = ar_hand_anchor_create();
60 m_handAnchors[Hand::RightHand] = ar_hand_anchor_create();
64 qCDebug(lcQuick3DXr) << Q_FUNC_INFO <<
", Initialized: " << m_initialized;
73 for (QQuick3DXrController *controller : std::as_const(m_controllers)) {
74 if (QtQuick3DXr::handForController(controller->controller()) == hand && QtQuick3DXr::pose_cast(controller->poseSpace()) == poseSpace) {
75 controller->setPosition(position);
76 controller->setRotation(rotation);
84 m_poseUsageDirty =
true;
85 if (controller->controller() == QQuick3DXrController::ControllerNone) {
86 m_controllers.remove(controller);
90 m_controllers.insert(controller);
95 m_poseUsageDirty = m_controllers.remove(controller);
100 QSSG_ASSERT(uint(hand) < 2 && uint(poseSpace) < 2,
return false);
101 if (m_poseUsageDirty) {
102 std::fill_n(&m_poseInUse[0][0], 4,
false);
103 for (
const auto *controller : std::as_const(m_controllers)) {
104 m_poseInUse[uint(controller->controller())][uint(controller->poseSpace())] =
true;
106 m_poseUsageDirty =
false;
108 return m_poseInUse[uint(hand)][uint(poseSpace)];
113 return m_handInputState[Hand::LeftHand];
118 return m_handInputState[Hand::RightHand];
123 return inputManager->d_func();
131static bool setupJoint(ar_hand_skeleton_joint_name_t jointName,
const ar_hand_skeleton_t handSkeleton,
const simd_float4x4 handTransform, QVector3D &jointPosition, QQuaternion &jointRotation)
133 bool isTracked =
false;
134 ar_skeleton_joint_t joint = ar_hand_skeleton_get_joint_named(handSkeleton, jointName);
135 if (joint !=
nullptr) {
136 if (ar_skeleton_joint_is_tracked(joint)) {
137 simd_float4x4 jointTransform = ar_skeleton_joint_get_anchor_from_joint_transform(joint);
138 jointTransform = simd_mul(handTransform, jointTransform);
140 QMatrix4x4 transform{jointTransform.columns[0].x, jointTransform.columns[1].x, jointTransform.columns[2].x, jointTransform.columns[3].x,
141 jointTransform.columns[0].y, jointTransform.columns[1].y, jointTransform.columns[2].y, jointTransform.columns[3].y,
142 jointTransform.columns[0].z, jointTransform.columns[1].z, jointTransform.columns[2].z, jointTransform.columns[3].z,
143 0.0f, 0.0f, 0.0f, 1.0f};
149 QSSGUtils::mat44::decompose(transform, jp, scale, jr);
152 jointPosition = jp * 100.0f;
162using HandJointList = QVarLengthArray<ar_hand_skeleton_joint_name_t, 28>;
170 static const HandJointList jointNames {
171 ar_hand_skeleton_joint_name_forearm_arm,
174 ar_hand_skeleton_joint_name_wrist,
175 ar_hand_skeleton_joint_name_forearm_wrist,
178 ar_hand_skeleton_joint_name_thumb_knuckle,
179 ar_hand_skeleton_joint_name_thumb_intermediate_base,
180 ar_hand_skeleton_joint_name_thumb_intermediate_tip,
181 ar_hand_skeleton_joint_name_thumb_tip,
184 ar_hand_skeleton_joint_name_index_finger_metacarpal,
185 ar_hand_skeleton_joint_name_index_finger_knuckle,
186 ar_hand_skeleton_joint_name_index_finger_intermediate_base,
187 ar_hand_skeleton_joint_name_index_finger_intermediate_tip,
188 ar_hand_skeleton_joint_name_index_finger_tip,
191 ar_hand_skeleton_joint_name_middle_finger_metacarpal,
192 ar_hand_skeleton_joint_name_middle_finger_knuckle,
193 ar_hand_skeleton_joint_name_middle_finger_intermediate_base,
194 ar_hand_skeleton_joint_name_middle_finger_intermediate_tip,
195 ar_hand_skeleton_joint_name_middle_finger_tip,
198 ar_hand_skeleton_joint_name_ring_finger_metacarpal,
199 ar_hand_skeleton_joint_name_ring_finger_knuckle,
200 ar_hand_skeleton_joint_name_ring_finger_intermediate_base,
201 ar_hand_skeleton_joint_name_ring_finger_intermediate_tip,
202 ar_hand_skeleton_joint_name_ring_finger_tip,
205 ar_hand_skeleton_joint_name_little_finger_metacarpal,
206 ar_hand_skeleton_joint_name_little_finger_knuckle,
207 ar_hand_skeleton_joint_name_little_finger_intermediate_base,
208 ar_hand_skeleton_joint_name_little_finger_intermediate_tip,
209 ar_hand_skeleton_joint_name_little_finger_tip,
217 qsizetype index = -1;
218 const auto &jointNames = getJointNameTable();
219 for (size_t i = 0, e = jointNames.size(); i != e && index == -1; ++i) {
220 if (jointNames[i] == jointName)
228static void getHandPose(ar_hand_skeleton_t handSkeleton,
const simd_float4x4 handTransform, QVector3D &posePosition, QQuaternion &poseRotation)
230 QMatrix4x4 qHandTransform{handTransform.columns[0].x, handTransform.columns[1].x, handTransform.columns[2].x, handTransform.columns[3].x,
231 handTransform.columns[0].y, handTransform.columns[1].y, handTransform.columns[2].y, handTransform.columns[3].y,
232 handTransform.columns[0].z, handTransform.columns[1].z, handTransform.columns[2].z, handTransform.columns[3].z,
233 0.0f, 0.0f, 0.0f, 1.0f};
234 simd_float4x4 rotHandTransform = { simd_float4{ qHandTransform(0,0), qHandTransform(1,0), qHandTransform(2,0), qHandTransform(3,0) },
235 simd_float4{ qHandTransform(0,1), qHandTransform(1,1), qHandTransform(2,1), qHandTransform(3,1) },
236 simd_float4{ qHandTransform(0,2), qHandTransform(1,2), qHandTransform(2,2), qHandTransform(3,2) },
237 simd_float4{ qHandTransform(0,3), qHandTransform(1,3), qHandTransform(2,3), qHandTransform(3,3) } };
241 setupJoint(ar_hand_skeleton_joint_name_index_finger_knuckle, handSkeleton, rotHandTransform, posePosition, poseRotation);
243 static QQuaternion rotation = QQuaternion::fromEulerAngles(QVector3D(0, 90, 90));
244 poseRotation = poseRotation * rotation;
247 setupJoint(ar_hand_skeleton_joint_name_middle_finger_knuckle, handSkeleton, rotHandTransform, posePosition, poseRotation);
249 static QQuaternion rotation = QQuaternion::fromEulerAngles(QVector3D(0, 180, 90));
250 poseRotation = poseRotation * rotation;
262 {
"", 0.0, QQuick3DXrInputAction::CustomAction },
263 {
"index_pinch", 0.005, QQuick3DXrInputAction::IndexFingerPinch },
264 {
"middle_pinch", 0.009, QQuick3DXrInputAction::MiddleFingerPinch },
265 {
"ring_pinch", 0.009, QQuick3DXrInputAction::RingFingerPinch },
266 {
"little_pinch", 0.01, QQuick3DXrInputAction::LittleFingerPinch },
279 const ar_hand_skeleton_joint_name_t pinchJoints[] { ar_hand_skeleton_joint_name_thumb_tip,
280 ar_hand_skeleton_joint_name_index_finger_tip,
281 ar_hand_skeleton_joint_name_middle_finger_tip,
282 ar_hand_skeleton_joint_name_ring_finger_tip,
283 ar_hand_skeleton_joint_name_little_finger_tip };
285 constexpr size_t pinchJointCount = std::size(pinchJoints);
287 simd_float4x4 jointTransforms[pinchJointCount];
289 bool isTracked[pinchJointCount] {
true,
false,
false,
false };
291 for (
int i = 0, end = pinchJointCount; isTracked[ThumbTip] && i != end; ++i) {
292 ar_hand_skeleton_joint_name_t jointName = pinchJoints[i];
293 if (ar_skeleton_joint_t joint = ar_hand_skeleton_get_joint_named(handSkeleton, jointName)) {
294 if (ar_skeleton_joint_is_tracked(joint)) {
295 jointTransforms[i] = ar_skeleton_joint_get_anchor_from_joint_transform(joint);
302 if (!isTracked[ThumbTip])
306 const simd_float4 thumbTip = jointTransforms[ThumbTip].columns[3];
309 for (
int i = 1, end = pinchJointCount; i != end; ++i) {
313 const simd_float4 diff = jointTransforms[i].columns[3] - thumbTip;
314 const float distance = simd_length(diff);
322 static const qsizetype idx = getJointIndex(ar_hand_skeleton_joint_name_index_finger_tip);
328 if (!m_isHandTrackingSupported)
331 QSSG_ASSERT(m_handTrackingProvider !=
nullptr,
return);
332 QSSG_ASSERT(m_handAnchors[Hand::LeftHand] !=
nullptr && m_handAnchors[Hand::RightHand] !=
nullptr,
return);
334 ar_hand_tracking_provider_get_latest_anchors(m_handTrackingProvider, m_handAnchors[Hand::LeftHand], m_handAnchors[Hand::RightHand]);
337 ar_hand_skeleton_t handSkeletons[2] {};
338 uint64_t handJointCount = 0;
339 for (
const auto hand : { Hand::LeftHand, Hand::RightHand }) {
340 handSkeletons[hand] = ar_hand_anchor_get_hand_skeleton(m_handAnchors[hand]);
341 handJointCount = qMax(handJointCount, ar_hand_skeleton_get_joint_count(handSkeletons[hand]));
344 const auto &jointNames = getJointNameTable();
346 QSSG_CHECK(handJointCount <= size_t(jointNames.size()));
348 for (
const auto hand : { Hand::LeftHand, Hand::RightHand }) {
349 const auto handSkeleton = handSkeletons[hand];
350 if (handSkeleton ==
nullptr) {
351 m_handInputState[hand]->setIsHandTrackingActive(
false);
356 auto &jpositions = jcache[hand].positions;
357 auto &jrotations = jcache[hand].rotations;
362 ar_skeleton_joint_t wristJoinOrigin = ar_hand_skeleton_get_joint_named(handSkeleton, ar_hand_skeleton_joint_name_wrist);
363 const bool isWristTracked = ar_skeleton_joint_is_tracked(wristJoinOrigin);
366 const simd_float4x4 handTransform = ar_anchor_get_origin_from_anchor_transform(m_handAnchors[hand]);
369 for (
auto jointName : jointNames) {
370 QVector3D jointPosition;
371 QQuaternion jointRotation;
372 if (setupJoint(jointName, handSkeleton, handTransform, jointPosition, jointRotation)) {
373 jpositions.append(jointPosition);
374 jrotations.append(jointRotation);
379 detectGestures(handSkeleton, hand);
383 if (isPoseInUse(hand, HandPoseSpace::AimPose) ) {
384 QVector3D handPosition;
385 QQuaternion handRotation;
386 getHandPose<HandPoseSpace::AimPose>(handSkeleton, handTransform, handPosition, handRotation);
388 setPosePositionAndRotation(hand, HandPoseSpace::AimPose, handPosition, handRotation);
389 }
else if (isPoseInUse(hand, HandPoseSpace::GripPose)) {
390 QVector3D handPosition;
391 QQuaternion handRotation;
392 getHandPose<HandPoseSpace::GripPose>(handSkeleton, handTransform, handPosition, handRotation);
394 setPosePositionAndRotation(hand, HandPoseSpace::GripPose, handPosition, handRotation);
397 m_handInputState[hand]->setJointPositionsAndRotations(jpositions, jrotations);
399 m_handInputState[hand]->setIsActive(isWristTracked);
446 static qint64 lastId = -1;
448 QJsonArray eventArray = events.value(QStringLiteral(
"events")).toArray();
449 for (
const auto &event : eventArray) {
450 QJsonObject eventObj = event.toObject();
454 const qint64 id = eventObj.value(QStringLiteral(
"id")).toDouble();
458 const QString kind = eventObj.value(QStringLiteral(
"kind")).toString();
459 if (kind != QStringLiteral(
"indirectPinch"))
460 qWarning() <<
"kind is " << kind <<
"!";
464 const QString phase = eventObj.value(QStringLiteral(
"phase")).toString();
467 QJsonObject selectionRayObj = eventObj.value(QStringLiteral(
"selectionRay")).toObject();
468 if (!selectionRayObj.isEmpty()) {
470 QJsonObject originObj = selectionRayObj.value(QStringLiteral(
"origin")).toObject();
471 QVector3D origin(originObj.value(QStringLiteral(
"x")).toDouble(), originObj.value(QStringLiteral(
"y")).toDouble(), originObj.value(QStringLiteral(
"z")).toDouble());
476 QJsonObject directionObj = selectionRayObj.value(QStringLiteral(
"direction")).toObject();
477 QVector3D direction(directionObj.value(QStringLiteral(
"x")).toDouble(), directionObj.value(QStringLiteral(
"y")).toDouble(), directionObj.value(QStringLiteral(
"z")).toDouble());
479 QEvent::Type eventType;
481 if (phase == QStringLiteral(
"active")) {
485 eventType = QEvent::MouseButtonPress;
488 eventType = QEvent::MouseMove;
493 eventType = QEvent::MouseButtonRelease;
496 QMouseEvent *event =
new QMouseEvent(eventType, QPointF(), QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
497 vrViewport.processPointerEventFromRay(origin, direction, event);
QQuick3DXrInputAction::Action type
const float pinchDistanceThreshold