ARCore для Jetpack XR может предоставлять информацию об обнаруженных руках пользователя, а также сведения о позах рук и связанных с ними суставов. Эти данные можно использовать, чтобы прикреплять объекты и модели к рукам пользователя, например меню инструментов:
Как получить доступ к сеансу
Получить информацию о руках можно с помощью среды выполнения Jetpack XR Session, которую может создать ваше приложение.
Как настроить сеанс
Отслеживание рук по умолчанию отключено в сеансах XR. Чтобы получать данные о руках, настройте сеанс и задайте режим HandTrackingMode.BOTH:
val newConfig = Config.Builder(session.config) .setHandTracking(HandTrackingMode.BOTH) .build() when (val result = session.configure(newConfig)) { is SessionConfigureSuccess -> TODO(/* Success! */) else -> TODO(/* The session could not be configured. See SessionConfigureResult for possible causes. */) }
Как получить данные о руках
Данные о руках доступны отдельно для левой и правой руки. Используйте каждую руку 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) }
У рук есть следующие свойства:
trackingState– отслеживается ли рука.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.setEnabled(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.setEnabled(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.getPrimaryHandSide(activity.contentResolver) val secondaryHand = if (handedness == HandSide.LEFT) Hand.right(session) else Hand.left(session) detectGesture(secondaryHand.state)
OpenXR™ и логотип OpenXR являются товарными знаками, принадлежащими компании Khronos Group Inc. и зарегистрированными в Китае, Европейском союзе, Японии и Великобритании.