Jetpack XR के लिए ARCore का इस्तेमाल करके, हाथों से काम करना

Jetpack XR के लिए ARCore, उपयोगकर्ता के हाथों के बारे में जानकारी दे सकता है. साथ ही, हाथों और उनसे जुड़े जोड़ों के पोज़ की जानकारी भी देता है. हाथ के इस डेटा का इस्तेमाल, उपयोगकर्ता के हाथों पर इकाइयों और मॉडल को अटैच करने के लिए किया जा सकता है. उदाहरण के लिए, टूल मेन्यू:

सेशन पाना

Android XR Session की मदद से, हाथ की जानकारी ऐक्सेस करें. Session पाने के लिए, सेशन के लाइफ़साइकल को समझना देखें.

सेशन कॉन्फ़िगर करना

XR सेशन में, हाथ ट्रैक करने की सुविधा डिफ़ॉल्ट रूप से चालू नहीं होती. हाथ का डेटा पाने के लिए, सेशन को कॉन्फ़िगर करें:

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! */)
}

हाथ का डेटा पाना

हाथ का डेटा, बाएं और दाएं हाथ के लिए अलग-अलग उपलब्ध होता है. हर जोड़ की पोज़िशन ऐक्सेस करने के लिए, हर हाथ के 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)
}

हाथों की ये प्रॉपर्टी होती हैं:

  • isActive: हाथ को ट्रैक किया जा रहा है या नहीं.
  • 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.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))

इसके अलावा, अपने दाएँ हाथ की इंडेक्स फ़िंगर की नोक पर मॉडल अटैच करने के लिए:

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))

हाथ के बुनियादी जेस्चर का पता लगाना

हाथ के बुनियादी जेस्चर का पता लगाने के लिए, हाथ के जोड़ों की पोज़ का इस्तेमाल करें. हाथ के जोड़ों के कॉन्वेंशन देखें और यह तय करें कि किसी पोज़ के तौर पर रजिस्टर करने के लिए, जोड़ों को किस तरह के पोज़ में होना चाहिए.

उदाहरण के लिए, अंगूठे और इंडेक्स फ़िंगर से पिंच करने का पता लगाने के लिए, दोनों उंगलियों के जोड़ों के बीच की दूरी का इस्तेमाल करें:

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.getHandedness(activity.contentResolver)
val secondaryHand = if (handedness == Hand.Handedness.LEFT) Hand.right(session) else Hand.left(session)
val handState = secondaryHand?.state ?: return
detectGesture(handState)