ARCore pour Jetpack XR peut fournir des informations sur les mains détectées de l'utilisateur et des informations sur la pose des mains et de leurs articulations associées. Ces données sur les mains peuvent être utilisées pour associer des entités et des modèles aux mains d'un utilisateur, par exemple un menu d'outils :
Obtenir une session
Accédez aux informations sur les mains via un Session
Android XR. Consultez Comprendre le cycle de vie d'une session pour obtenir un Session
.
Configurer la session
Le suivi des mains n'est pas activé par défaut dans les sessions XR. Pour recevoir des données sur les mains, configurez la session et définissez le mode HandTrackingMode.BOTH
:
val newConfig = session.config.copy( handTracking = Config.HandTrackingMode.BOTH ) when (val result = session.configure(newConfig)) { is SessionConfigureConfigurationNotSupported -> TODO(/* Some combinations of configurations are not valid. Handle this failure case. */) is SessionConfigureSuccess -> TODO(/* Success! */) else -> TODO(/* A different unhandled exception was thrown. */) }
Récupérer les données des mains
Les données de la main sont disponibles séparément pour la main gauche et la main droite. Utilisez state
pour chaque main afin d'accéder aux positions de pose de chaque articulation :
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) }
Les mains ont les propriétés suivantes :
trackingState
: indique si la main est suivie ou non.handJoints
: carte des articulations de la main aux poses. Les poses des articulations de la main sont spécifiées par les normes OpenXR.
Utiliser les données des mains dans votre application
Les positions des articulations de la main d'un utilisateur peuvent être utilisées pour ancrer des objets 3D aux mains d'un utilisateur, par exemple pour attacher un modèle à la paume gauche :
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))
Pour attacher un modèle à l'extrémité de l'index droit :
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))
Détecter les gestes de la main de base
Utilisez les poses des articulations de la main pour détecter les gestes de base de la main. Consultez les conventions relatives aux articulations de la main pour déterminer la plage de poses dans laquelle les articulations doivent se trouver pour être enregistrées comme une pose donnée.
Par exemple, pour détecter un pincement avec le pouce et l'index, utilisez la distance entre les deux articulations des doigts :
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
Le geste "Arrêt" est un exemple de geste plus complexe. Dans ce geste, chaque doigt doit être tendu, c'est-à-dire que chaque articulation de chaque doigt doit être à peu près orientée dans la même direction :
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)
Lorsque vous développez une détection personnalisée pour les gestes de la main, tenez compte des points suivants :
- Les utilisateurs peuvent interpréter différemment un geste donné. Par exemple, certains peuvent considérer qu'un geste d'arrêt consiste à écarter les doigts, tandis que d'autres peuvent trouver plus intuitif de les garder serrés.
- Certains gestes peuvent être difficiles à maintenir. Utilisez des gestes intuitifs qui ne fatiguent pas les mains de l'utilisateur.
Déterminer la main secondaire de l'utilisateur
Le système Android place la navigation système sur la main principale de l'utilisateur, comme spécifié par l'utilisateur dans les préférences système. Utilisez la main secondaire pour vos gestes personnalisés afin d'éviter les conflits avec les gestes de navigation du système :
val handedness = Hand.getPrimaryHandSide(activity.contentResolver) val secondaryHand = if (handedness == Hand.HandSide.LEFT) Hand.right(session) else Hand.left(session) val handState = secondaryHand?.state ?: return detectGesture(handState)
OpenXR™ et le logo OpenXR sont des marques déposées appartenant à The Khronos Group Inc. et sont enregistrées en Chine, dans l'Union européenne, au Japon et au Royaume-Uni.