ARCore pour Jetpack XR peut fournir des informations sur les mains détectées de l'utilisateur, et fournit des informations sur la position des mains et des 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 la section 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 de main, configurez la session:
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! */) }
Récupérer les données des mains
Les données sur les mains sont disponibles séparément pour la main gauche et la main droite. Utilisez le state
de chaque main pour accéder aux positions de pose pour 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:
isActive
: indique si la main est suivie ou non.handJoints
: mappe des articulations des mains aux poses. Les poses des articulations des mains sont spécifiées par les normes OpenXR.
Utiliser les données de la main dans votre application
Les positions des articulations des mains d'un utilisateur peuvent être utilisées pour ancrer des objets 3D à ses mains, 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.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))
Pour associer un modèle à l'extrémité de l'index de votre main droite:
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))
Détecter les gestes de base des mains
Utilisez les positions des articulations de la main pour détecter les gestes de base. Consultez les conventions concernant les articulations des mains pour déterminer la plage de poses dans laquelle les articulations doivent se trouver pour être enregistrées en tant que pose donnée.
Par exemple, pour détecter un pincement avec le pouce et l'index, utilisez la distance entre les deux articulations des pointes:
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 pointer à peu près 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)
Tenez compte des points suivants lorsque vous développez une détection personnalisée pour les gestes des mains:
- Les utilisateurs peuvent interpréter différemment un geste donné. Par exemple, certains peuvent considérer qu'un geste de "stop" consiste à écarter les doigts, tandis que d'autres peuvent trouver plus intuitif de les rapprocher.
- Certains gestes peuvent être inconfortables à 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 indiqué 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 système:
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)