ARCore для Jetpack XR может предоставить информацию об обнаруженных руках пользователя и дает информацию о позе для рук и связанных с ними суставов. Эти данные о руках могут быть использованы для присоединения сущностей и моделей к рукам пользователя, например, меню инструментов:
Получить сессию
Доступ к информации о руке через Session
Android XR. См. раздел Понимание жизненного цикла сеанса , чтобы получить Session
.
Настройте сеанс
Отслеживание рук по умолчанию не включено в сеансах XR. Чтобы получать данные рук, настройте сеанс:
val newConfig = session.config.copy( handTracking = Config.HandTrackingMode.Enabled ) when (val result = session.configure(newConfig)) { is SessionConfigureConfigurationNotSupported -> TODO(/* Some combinations of configurations are not valid. Handle this failure case. */) is SessionConfigurePermissionsNotGranted -> TODO(/* The required permissions in result.permissions have not been granted. */) is SessionConfigureSuccess -> TODO(/* Success! */) }
Получить данные руки
Данные о руках доступны отдельно для левой и правой руки. Используйте state
каждой руки для доступа к позициям позы для каждого сустава:
Hand.left(session)?.state?.collect { handState -> // or Hand.right(session) // Hand state has been updated. // Use the state of hand joints to update an entity's position. renderPlanetAtHandPalm(handState) }
Руки обладают следующими свойствами:
-
isActive
: отслеживается ли рука. handJoints
: карта суставов руки для поз. Позы суставов руки определены стандартами OpenXR .
Используйте данные рук в своем приложении
Положение суставов рук пользователя можно использовать для прикрепления 3D-объектов к рукам пользователя, например, для прикрепления модели к левой ладони:
val palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return // the down direction points in the same direction as the palm val angle = Vector3.angleBetween(palmPose.rotation * Vector3.Down, Vector3.Up) palmEntity.setHidden(angle > Math.toRadians(40.0)) val transformedPose = session.scene.perceptionSpace.transformPoseTo( palmPose, session.scene.activitySpace, ) val newPosition = transformedPose.translation + transformedPose.down * 0.05f palmEntity.setPose(Pose(newPosition, transformedPose.rotation))
Или прикрепить модель к кончику указательного пальца правой руки:
val tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return // the forward direction points towards the finger tip. val angle = Vector3.angleBetween(tipPose.rotation * Vector3.Forward, Vector3.Up) indexFingerEntity.setHidden(angle > Math.toRadians(40.0)) val transformedPose = session.scene.perceptionSpace.transformPoseTo( tipPose, session.scene.activitySpace, ) val position = transformedPose.translation + transformedPose.forward * 0.03f val rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up) indexFingerEntity.setPose(Pose(position, rotation))
Распознавать основные жесты рук
Используйте позы суставов руки для обнаружения основных жестов руки. Ознакомьтесь с Конвенциями суставов руки , чтобы определить, в каком диапазоне поз должны находиться суставы, чтобы зарегистрироваться как заданная поза.
Например, чтобы обнаружить сжатие большим и указательным пальцами, используйте расстояние между двумя кончиками пальцев:
val thumbTip = handState.handJoints[HandJointType.THUMB_TIP] ?: return false val thumbTipPose = session.scene.perceptionSpace.transformPoseTo(thumbTip, session.scene.activitySpace) val indexTip = handState.handJoints[HandJointType.INDEX_TIP] ?: return false val indexTipPose = session.scene.perceptionSpace.transformPoseTo(indexTip, session.scene.activitySpace) return Vector3.distance(thumbTipPose.translation, indexTipPose.translation) < 0.05
Примером более сложного жеста является жест «стоп». В этом жесте каждый палец должен быть вытянут, то есть каждый сустав каждого пальца должен быть примерно направлен в одном направлении:
val threshold = toRadians(angleInDegrees = 30f) fun pointingInSameDirection(joint1: HandJointType, joint2: HandJointType): Boolean { val forward1 = handState.handJoints[joint1]?.forward ?: return false val forward2 = handState.handJoints[joint2]?.forward ?: return false return Vector3.angleBetween(forward1, forward2) < threshold } return pointingInSameDirection(HandJointType.INDEX_PROXIMAL, HandJointType.INDEX_TIP) && pointingInSameDirection(HandJointType.MIDDLE_PROXIMAL, HandJointType.MIDDLE_TIP) && pointingInSameDirection(HandJointType.RING_PROXIMAL, HandJointType.RING_TIP)
При разработке индивидуального распознавания жестов рук следует учитывать следующие моменты:
- Пользователи могут по-разному интерпретировать тот или иной жест. Например, некоторые могут считать жестом «стоп» растопыренные пальцы, в то время как другие могут посчитать более интуитивным сжатие пальцев вместе.
- Некоторые жесты могут быть неудобны для поддержания. Используйте интуитивные жесты, которые не напрягают руки пользователя.
Определите вторую руку пользователя
Система Android помещает системную навигацию на первичную руку пользователя, как указано пользователем в системных настройках. Используйте вторичную руку для ваших пользовательских жестов, чтобы избежать конфликтов с системными навигационными жестами:
val handedness = Hand.getHandedness(activity.contentResolver) val secondaryHand = if (handedness == Hand.Handedness.LEFT) Hand.right(session) else Hand.left(session) val handState = secondaryHand?.state ?: return detectGesture(handState)
OpenXR™ и логотип OpenXR являются товарными знаками компании Khronos Group Inc. и зарегистрированы в качестве товарного знака в Китае, Европейском Союзе, Японии и Великобритании.