ARCore para Jetpack XR puede proporcionar información sobre las manos detectadas del usuario y brindar información sobre la postura de las manos y sus articulaciones asociadas. Estos datos de la mano se pueden usar para adjuntar entidades y modelos a las manos de un usuario, por ejemplo, un menú de herramientas:
Obtén una sesión
Accede a la información de la mano a través de un Session
de Android XR. Consulta Comprende el ciclo de vida de una sesión para obtener un Session
.
Configura la sesión
El seguimiento de manos no está habilitado de forma predeterminada en las sesiones de XR. Para recibir datos de la mano, configura la sesión:
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! */) }
Cómo recuperar datos de la mano
Los datos de la mano están disponibles para la mano izquierda y la derecha por separado. Usa el state
de cada mano para acceder a las posiciones de pose de cada articulación:
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) }
Las manos tienen las siguientes propiedades:
isActive
: Indica si se está haciendo un seguimiento de la mano.handJoints
: Es un mapa de las articulaciones de la mano a las poses. Las posiciones de las articulaciones de la mano se especifican en los estándares de OpenXR.
Usa datos de la mano en tu app
Las posiciones de las articulaciones de la mano de un usuario se pueden usar para anclar objetos 3D a las manos de un usuario, por ejemplo, para conectar un modelo a la palma izquierda:
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))
O bien, para colocar un modelo en la punta del dedo índice de la mano derecha, haz lo siguiente:
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))
Cómo detectar gestos básicos con las manos
Usa las posiciones de las articulaciones de la mano para detectar gestos básicos de la mano. Consulta las Convenciones de las articulaciones de la mano para determinar en qué rango de posiciones deben estar las articulaciones para registrarse como una posición determinada.
Por ejemplo, para detectar un pellizco con el pulgar y el dedo índice, usa la distancia entre las dos articulaciones de la punta:
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
Un ejemplo de un gesto más complicado es el gesto de "detener". En este gesto, cada dedo debe estar extendido, es decir, cada articulación de cada dedo debe apuntar aproximadamente en la misma dirección:
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)
Ten en cuenta los siguientes puntos cuando desarrolles la detección personalizada de gestos con la mano:
- Los usuarios pueden tener una interpretación diferente de cualquier gesto determinado. Por ejemplo, algunos pueden considerar que un gesto de "detener" es tener los dedos extendidos, mientras que a otros les puede resultar más intuitivo tener los dedos juntos.
- Es posible que algunos gestos sean incómodos de mantener. Usa gestos intuitivos que no fatiguen las manos del usuario.
Determina la mano secundaria del usuario
El sistema Android coloca la navegación del sistema en la mano principal del usuario, como este lo especifica en las preferencias del sistema. Usa la mano secundaria para tus gestos personalizados para evitar conflictos con los gestos de navegación del sistema:
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)