ARCore per Jetpack XR può fornire informazioni sulle mani rilevate dell'utente e fornisce informazioni sulla postura delle mani e delle articolazioni associate. Questi dati della mano possono essere utilizzati per collegare entità e modelli alle mani di un utente, ad esempio un menu degli strumenti:
Ottenere una sessione
Accedi alle informazioni sulla mano tramite un Session
Android XR. Consulta la sezione Comprendere il ciclo di vita di una sessione per ottenere un Session
.
Configura la sessione
Il tracciamento delle mani non è attivato per impostazione predefinita nelle sessioni XR. Per ricevere i dati della mano,
configura la sessione e imposta la modalità 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. */) }
Recuperare i dati della mano
I dati della mano sono disponibili separatamente per la mano sinistra e la mano destra. Usa i
state
di ogni mano per accedere alle posizioni della posa per ogni articolazione:
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) }
Le mani hanno le seguenti proprietà:
trackingState
: indica se la mano viene tracciata o meno.handJoints
: una mappatura delle articolazioni della mano alle pose. Le pose delle articolazioni della mano sono specificate dagli standard OpenXR.
Utilizzare i dati della mano nell'app
Le posizioni delle articolazioni della mano di un utente possono essere utilizzate per ancorare oggetti 3D alle mani di un utente, ad esempio per collegare un modello al palmo sinistro:
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))
Oppure, per collegare un modello alla punta dell'indice della mano destra:
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))
Rilevare i gesti di base delle mani
Utilizza le pose delle articolazioni della mano per rilevare i gesti di base della mano. Consulta le Convenzioni delle articolazioni della mano per determinare in quale intervallo di pose devono trovarsi le articolazioni per essere registrate come una determinata posa.
Ad esempio, per rilevare un pizzico con il pollice e l'indice, utilizza la distanza tra le due punte delle dita:
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 esempio di gesto più complicato è il gesto "Stop". In questo gesto, ogni dito deve essere disteso, ovvero ogni articolazione di ogni dito deve essere orientata all'incirca nella stessa direzione:
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)
Quando sviluppi il rilevamento personalizzato per i gesti della mano, tieni presente quanto segue:
- Gli utenti potrebbero interpretare in modo diverso una determinata azione. Ad esempio, alcuni potrebbero considerare il gesto di "stop" con le dita divaricate, mentre altri potrebbero trovarlo più intuitivo con le dita vicine.
- Alcuni gesti potrebbero essere scomodi da mantenere. Utilizza gesti intuitivi che non affaticano le mani dell'utente.
Determinare la mano secondaria dell'utente
Il sistema Android posiziona la navigazione di sistema sulla mano principale dell'utente, come specificato dall'utente nelle preferenze di sistema. Usa la mano secondaria per i tuoi gesti personalizzati per evitare conflitti con i gesti di navigazione del sistema:
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™ e il logo OpenXR sono marchi di proprietà di The Khronos Group Inc. e sono registrati come marchi in Cina, nell'Unione Europea, in Giappone e nel Regno Unito.