ofreezy
ofreezy

Reputation: 31

How to add tap listener on childNodes of ModelNode?

I am currently working on an AR Application for Android with Kotlin and JetPack Compose using SceneView for AR and the 3D Rendering.

The Problem

At one point i have to display a 3D Model, which has certain nodes that i want to listen on for interactions. So i added an onSingleTapUp-Event onto the specific childNode. This Event gets never triggered. I made some further checks and i guess the problem is, that the GestureListener only detects the first Node the raycast hits.

Does anyone know how to solve this issue?

My Code:


        val modelNode = ModelNode(
            modelInstance = modelLoader.createModelInstance(assetFileLocation = mainModel),
            scaleToUnits = 0.21f,
            centerOrigin = Position(0f)
        ).apply{
            setPriority(0) // set Priority to 0 for preferring hit tests on other nodes
            position = Position(0.21f/2,0f,0.29f/1.5f)
            rotation = Rotation(0f, -120f, 0f)
            Log.d("NodeDebug", "Creating FinalModelExperienceNode with size: $size at position: $position")
        }

        //intially hide all nodes but the 'BikeFull'-Node
        modelNode.nodes.entries.forEach { entry ->
            // Always show the BikeFull node
            if (entry.key.contains("BikeFull")) {
                entry.value.isVisible = true
                Log.d("NodeDebug", "entry: ${entry.key}, ${entry.value}")
            } else {
                entry.value.isVisible = false
            }
        }
        
        //set visibillity and animations for reward nodes
        experienceState.rewards.forEachIndexed { index, reward ->
            val rewardNode = modelNode.nodes.entries.find { it.key == reward.standardNode }
            val rewardNodePuls = modelNode.nodes.entries.find { it.key == reward.blinkingNode }
            if (rewardNode != null && rewardNodePuls != null) {
                if(reward.display == RewardDisplay.VISIBLE){
                    rewardNode.value.isVisible = true
                    rewardNodePuls.value.isVisible = false
                }
                if(reward.display == RewardDisplay.INVISIBLE){
                    rewardNode.value.isVisible = false
                    rewardNodePuls.value.isVisible = false
                }
                if(reward.display == RewardDisplay.BLINKING) {
                    var isBlinking = true
                    rewardNode.value.isVisible = true
                    rewardNodePuls.value.isVisible = true
                    rewardNodePuls.value.childNodes.forEach {
                        it.isVisible = true
                    }
                    rewardNode.value.onSingleTapUp = { // <-- this gets never executed, also if i try onTouch for example
                        Log.d("NodeDebug", "tapped RewardNode: $rewardNode")
                        isBlinking = false
                        reward.display = RewardDisplay.VISIBLE
                        onStateChange(experienceState.copy())
                        true
                    }
                    CoroutineScope(Dispatchers.Main).launch {
                        while (isBlinking) {
                            rewardNodePuls.value.isVisible = !rewardNodePuls.value.isVisible
                            rewardNodePuls.value.childNodes.forEach {
                                it.isVisible = rewardNodePuls.value.isVisible
                            }
                            delay(1000L) // 1 second delay
                        }
                    }
                }
            }
        }

Adding GestureListener to Scene

   ARScene(
        ...
            onGestureListener = rememberOnGestureListener(
                onSingleTapUp = { _, node ->
                    Log.d("NodeDebug", " onSingleTapUp Node: $node")
                }
            ),
        ...
  )

It always just registers an singleTapUp for the ModelNode and so the specific event for the childNode of ModelNode doesn't get triggered

Upvotes: 3

Views: 65

Answers (0)

Related Questions