본문 바로가기
Android/기술 구현 공부(AR core)

Android Studio, Kotlin, Sceneform] SceneView에서 transformableNode 사용하기

by 김마리님 2021. 3. 25.

AR관련 jetpack으로 3D 입체를 랜더링 하는 중이다.

진짜 이게 웃기는게 ArScneneView와 SceneView 두 가지가 있다.

구글 ARcore, Sceneform에는 핀치, 드래그에 대응하는 Zoom, Quatation, transform을 담당해주는 노드가 따로 있는데, 이게 transformableNode이다.

근데 이게 단순히 랜더링만 하고 싶어 사용하는 sceceView에는 사용을 할 수가 없다(ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ) 

그럼 ArSceneView 사용하면 되지 모가문제임? 

나도 그렇게 생각해서 ArScneneView 사용해봤다. 카메라를 필수적으로 연결해야한다.

OMG ..........

 

그러나 우리가 누구인가 개발자들을 어케든 방법을 찾아낸다.

이게.. 사용을 아주 못하는건 아니고, 야매로, 진짜 임시적으로 사용할 수 있는 법이 있긴 한데.

 

- MainActivity.kt

    private fun addLineBetweenPoints(from: Vector3, to: Vector3, colorCode: String) {
        // Node that is automatically positioned in world space based on the ARCore Anchor.
        transformableNode = TransformableNode(transformationSystem)
        transformableNode.setParent(parentsTransformableNode)


        // Compute a line's length
        val lineLength = Vector3.subtract(from, to).length()

        // Prepare a color
        val colorCode = Color(android.graphics.Color.parseColor(colorCode))

        RenderingUtil.drawCylinderLine(
            this,
            colorCode,
            0.0025f * cylinderDiameter,
            lineLength,
            transformableNode,
            from,
            to
        )
    }

 

- RenderingUtil.kt (Object class)

   fun drawCylinderLine(
        context: Context,
        lineColor: Color,
        radius: Float,
        length: Float,
        parentNode: TransformableNode,
        from: Vector3,
        to: Vector3
    ) {
        // 1. make a material by the color
        MaterialFactory.makeOpaqueWithColor(context, lineColor)
            .thenAccept { material: Material? ->
                // 2. make a model by the material
                val model = ShapeFactory.makeCylinder(
                    radius, length,
                    Vector3(0f, 0f, 0f), material
                )
                model.isShadowReceiver = false
                model.isShadowCaster = true

                // 3. make node
                val node = Node()
                node.renderable = model

                node.setParent(parentNode)
                node.worldPosition = Vector3.add(to, from).scaled(.5f);

                //4. set rotation
                val difference = Vector3.subtract(to, from)
                val directionFromTopToBottom = difference.normalized()
                val rotationFromAToB =
                    Quaternion.lookRotation(
                        directionFromTopToBottom,
                        Vector3.up()
                    )
                node.worldRotation = Quaternion.multiply(
                    rotationFromAToB,
                    Quaternion.axisAngle(Vector3(1.0f, 0.0f, 0.0f), 90f)
                )
            }
    }

 

- MainActivity.kt

sceneView.scene.addOnPeekTouchListener { hitTestResult, motionEvent ->

                transformationSystem.onTouch(hitTestResult, motionEvent)

....

 

먼저 무식하게 transforamtionNode를 선언해놓고 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 랜더링 된 개체를 노드에 붙인다.

다음에 바로 리스너를 붙여주면 끝인데, 여기서 문제가 생긴다.

바로........ 

 

arsceneview로 캐스팅 할 수 없다고 한다.

근데 이거 되게 간단하게 해결할 수 있다. 지금 하얀줄로 로그 뜬거 보다시피, 원래 코드는ㅋㅋㅋㅋㅋ

 

sceneView.scene.addOnPeekTouchListener { hitTestResult, motionEvent ->

            try {

                transformationSystem.onTouch(hitTestResult, motionEvent)

                if (motionEvent.action == MotionEvent.ACTION_DOWN) {

                    downX = motionEvent.x
                    downY = motionEvent.y

                } else if (motionEvent.action == MotionEvent.ACTION_MOVE) {
                    if (motionEvent.pointerCount == 2) {
                        DlogUtil.d(TAG, "손가락 2개")

                        isScale = true;

                    } else {
                        DlogUtil.d(TAG, "손가락 1개")

                        if (isScale) {
                            return@addOnPeekTouchListener
                        }

                        if (abs(motionEvent.x - downX) > 40 || abs(motionEvent.y - downY) > 40) {

                            DlogUtil.d(TAG, "쿼터니언")

                            //개선 필요
                            var x: Float = motionEvent.x - downX
                            var y: Float = motionEvent.y - downY

                            var percentX: Float = x / sceneView.width * 0.5f
                            var percentY: Float = y / sceneView.height * 0.5f

                            xAngle = percentX * 360 * 0.52f + lastXAngle
                            yAngle = percentY * 360 * 0.52f + lastYAngle

                            var xQuaternion = Quaternion.axisAngle(Vector3(0f, 1f, 0f), xAngle)
                            //자바의 삼각함수는 라디언만 먹음
                            var yQuaternion = Quaternion.axisAngle(
                                Vector3(
                                    cos(Math.toRadians(xAngle.toDouble())).toFloat(),
                                    0f,
                                    sin(Math.toRadians(xAngle.toDouble())).toFloat()
                                ), yAngle
                            )

                            parentsTransformableNode.localRotation =
                                Quaternion.multiply(xQuaternion, yQuaternion)
                        }
                    }


                } else if (motionEvent.action == MotionEvent.ACTION_UP) {
                    DlogUtil.d(TAG, "손가락 뗐다고~~~")
                    if (lastDistance != 0f) {
                        lastDistance = 0f
                    }

                    lastXAngle = xAngle
                    lastYAngle = yAngle

                    isScale = false

                }

            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }

 

try-catch 구문으로 해결했다.

 

물론 이게 완벽한 방법은 아니고, 매끄럽게 돌아가긴 하지만 이미 그려진 랜더링을 터치할때마다 catch되면서 매끄럽게 움직이지 않기 때문에 최선의 방법은 아니다. 그러나 아예 사용하지 못하는 최악의 상황을 방지하는 차악이기 때문에.. 사실 수학적으로 쿼테이션은 어렵지 않은데 핀치가 문제인거라, 핀치를 계산하실 수 있는 분은 anchorNode의 좌표를 일일히 계산하셔서 핀치 될때마다 노드를 지우는 방식을 해도 괜찮지 않을까^^... (안괜찮아보임)

 

결과 :

 

보다시피 확대 회전 잘 되는 모습이다.

 

 

사실 cameraClip 이전에 부딪힌 문제인데, 이건 깃허브의 Hello_ar_java에 자주 출몰하는 이슈이기도 해서 기재 안하려고 하다가.. 한글로 기록된 블로그에서 ar 관련 이슈나 오류를 찾는게 너무너무너무너무먼ㅇ뭔ㅁ 어려워서.. 혹시 나같은 문제 겪는 한구긴 개발자 또 있을까봐... 써봄.......

 

반응형