1+ package com.example.xr.arcore
2+
3+ import android.annotation.SuppressLint
4+ import android.os.Bundle
5+ import androidx.activity.ComponentActivity
6+ import androidx.lifecycle.lifecycleScope
7+ import androidx.xr.arcore.Hand
8+ import androidx.xr.arcore.HandJointType
9+ import androidx.xr.compose.platform.setSubspaceContent
10+ import androidx.xr.runtime.Session
11+ import androidx.xr.runtime.math.Pose
12+ import androidx.xr.runtime.math.Quaternion
13+ import androidx.xr.runtime.math.Vector3
14+ import androidx.xr.scenecore.Entity
15+ import androidx.xr.scenecore.GltfModel
16+ import androidx.xr.scenecore.GltfModelEntity
17+ import kotlinx.coroutines.guava.await
18+ import kotlinx.coroutines.launch
19+
20+ class SampleHandsActivity : ComponentActivity () {
21+ lateinit var session: Session
22+ lateinit var scenecoreSession: androidx.xr.scenecore.Session
23+ lateinit var sessionHelper: SessionLifecycleHelper
24+
25+ var palmEntity: Entity ? = null
26+ var indexFingerEntity: Entity ? = null
27+
28+ override fun onCreate (savedInstanceState : Bundle ? ) {
29+ super .onCreate(savedInstanceState)
30+ setSubspaceContent { }
31+
32+ scenecoreSession = androidx.xr.scenecore.Session .create(this @SampleHandsActivity)
33+ lifecycleScope.launch {
34+ val model = GltfModel .create(scenecoreSession, " models/saturn_rings.glb" ).await()
35+ palmEntity = GltfModelEntity .create(scenecoreSession, model).apply {
36+ setScale(0.3f )
37+ setHidden(true )
38+ }
39+ indexFingerEntity = GltfModelEntity .create(scenecoreSession, model).apply {
40+ setScale(0.2f )
41+ setHidden(true )
42+ }
43+ }
44+
45+ sessionHelper = SessionLifecycleHelper (
46+ onCreateCallback = { session = it },
47+ onResumeCallback = {
48+ collectHands(session)
49+ }
50+ )
51+ lifecycle.addObserver(sessionHelper)
52+ }
53+ }
54+
55+ fun SampleHandsActivity.collectHands (session : Session ) {
56+ lifecycleScope.launch {
57+ // [START androidxr_arcore_hand_collect]
58+ Hand .left(session)?.state?.collect { handState -> // or Hand.right(session)
59+ // Hand state has been updated.
60+ // Use the state of hand joints to update an entity's position.
61+ renderPlanetAtHandPalm(handState)
62+ }
63+ // [END androidxr_arcore_hand_collect]
64+ }
65+ lifecycleScope.launch {
66+ Hand .right(session)?.state?.collect { rightHandState ->
67+ renderPlanetAtFingerTip(rightHandState)
68+ }
69+ }
70+ }
71+
72+ @SuppressLint(" RestrictedApi" ) // HandJointType is mistakenly @Restrict: b/397415504
73+ fun SampleHandsActivity.renderPlanetAtHandPalm (leftHandState : Hand .State ) {
74+ val palmEntity = palmEntity ? : return
75+ // [START androidxr_arcore_hand_entityAtHandPalm]
76+ val palmPose = leftHandState.handJoints[HandJointType .PALM ] ? : return
77+
78+ // the down direction points in the same direction as the palm
79+ val angle = Vector3 .angleBetween(palmPose.rotation * Vector3 .Down , Vector3 .Up )
80+ palmEntity.setHidden(angle > Math .toRadians(40.0 ))
81+
82+ val transformedPose =
83+ scenecoreSession.perceptionSpace.transformPoseTo(
84+ palmPose,
85+ scenecoreSession.activitySpace,
86+ )
87+ val newPosition = transformedPose.translation + transformedPose.down * 0.05f
88+ palmEntity.setPose(Pose (newPosition, transformedPose.rotation))
89+ // [END androidxr_arcore_hand_entityAtHandPalm]
90+ }
91+
92+ @SuppressLint(" RestrictedApi" ) // HandJointType is mistakenly @Restrict: b/397415504
93+ fun SampleHandsActivity.renderPlanetAtFingerTip (rightHandState : Hand .State ) {
94+ val indexFingerEntity = indexFingerEntity ? : return
95+
96+ // [START androidxr_arcore_hand_entityAtIndexFingerTip]
97+ val tipPose = rightHandState.handJoints[HandJointType .INDEX_TIP ] ? : return
98+
99+ // the forward direction points towards the finger tip.
100+ val angle = Vector3 .angleBetween(tipPose.rotation * Vector3 .Forward , Vector3 .Up )
101+ indexFingerEntity.setHidden(angle > Math .toRadians(40.0 ))
102+
103+ val transformedPose =
104+ scenecoreSession.perceptionSpace.transformPoseTo(
105+ tipPose,
106+ scenecoreSession.activitySpace,
107+ )
108+ val position = transformedPose.translation + transformedPose.forward * 0.03f
109+ val rotation = Quaternion .fromLookTowards(transformedPose.up, Vector3 .Up )
110+ indexFingerEntity.setPose(Pose (position, rotation))
111+ // [END androidxr_arcore_hand_entityAtIndexFingerTip]
112+ }
0 commit comments