Как перемещать и вращать SCNode, используя ARKit и Gesture Recognizer? - PullRequest
0 голосов
/ 22 мая 2018

Я работаю над приложением для iOS на основе AR, используя ARKit (SceneKit).Для этого я использовал образец кода Apple https://developer.apple.com/documentation/arkit/handling_3d_interaction_and_ui_controls_in_augmented_reality.Используя это я могу перемещать или вращать весь виртуальный объект.

Но я хочу выбрать и переместить / повернуть дочерний узел в виртуальном объекте с помощью пальца пользователя, аналогично тому, как мы перемещаем / вращаем сам весь виртуальный объект.

Я попробовал две нижеприведенные ссылки, но он только перемещает дочерний узел по определенной оси, но не перемещается никуда, когда пользователь перемещает палец.

ARKit - перетаскивание узла вдоль определенной оси (не на плоскости)

Перетаскивание SCNNode в ARKit с использованием SceneKit

Также я попытался заменить Виртуальный объект, который является SCNReferenceNode, на SCNode, чтобы любая функциональность, присутствующая в существующем Виртуальном объекте, применялась и к дочернему узлу, она не работала.

Может ли кто-нибудь помочь мне с тем, как свободно перемещать / вращать не только виртуальный объект, но и дочерний узел виртуального объекта?

Ниже приведен код, который я сейчас использую,

       let tapPoint: CGPoint = gesture.location(in: sceneView)
        let result = sceneView.hitTest(tapPoint, options: nil)
        if result.count == 0 {
            return
        }
        let scnHitResult: SCNHitTestResult? = result.first
        movedObject = scnHitResult?.node //.parent?.parent

        let hitResults = self.sceneView.hitTest(tapPoint, types: .existingPlane)
        if !hitResults.isEmpty{
            guard let hitResult = hitResults.last else { return }
            movedObject?.position = SCNVector3Make(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
        }

1 Ответ

0 голосов
/ 23 мая 2018

Чтобы переместить объект:

Выполните hitTest, чтобы проверить, где вы коснулись, и определить, к какой плоскости вы прикоснулись, и получить позицию.Переместите свой SCNNode в эту позицию, изменив значение node.position с помощью SCNVector3.

Код:

@objc func panDetected(recognizer: UIPanGestureRecognizer){
let hitResult = self.arSceneView.hitTest(loc, types: .existingPlane)
if !hitResult.isEmpty{
guard let hitResult = hitResult.last else { return }
self.yourNode.position = SCNVector3Make(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
}

Приведенного выше кода достаточно для перемещения вашего узла по обнаруженной плоскости, где бы вы ни находилисьприкоснуться, а не только по одной оси.

Поворот узла в соответствии с вашим жестом - очень сложная задача, и я довольно долго работал над решением, так и не достигнув идеального результата.Но я наткнулся на этот репозиторий в GitHub, который позволяет вам делать это с очень впечатляющим результатом.https://github.com/Xartec/ScreenSpaceRotationAndPan

Swift-версия кода, который требуется для поворота вашего узла с помощью вашего жеста, будет:

var previousLoc: CGPoint?
var touchCount: Int?

@objc func panDetected(recognizer: UIPanGestureRecognizer){

    let loc = recognizer.location(in: self.view)
    var delta = recognizer.translation(in: self.view)

    if recognizer.state == .began {
        previousLoc = loc
        touchCount = recognizer.numberOfTouches
    }
    else if gestureRecognizer.state == .changed {
        delta = CGPoint.init(x: 2 * (loc.x - previousLoc.x), y: 2 * (loc.y - previousLoc.y))
        previousLoc = loc
        if touchCount != recognizer.numberOfTouches {
            return
        }
        var rotMatrix: SCNMatrix4!
        let rotX = SCNMatrix4Rotate(SCNMatrix4Identity, Float((1.0/100) * delta.y), 1, 0, 0)
        let rotY = SCNMatrix4Rotate(SCNMatrix4Identity, Float((1.0 / 100) * delta.x), 0, 1, 0)
        rotMatrix = SCNMatrix4Mult(rotX, rotY)

        let transMatrix = SCNMatrix4MakeTranslation(yourNode.position.x, yourNode.position.y, yourNode.position.z)
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, SCNMatrix4Invert(transMatrix))
        let parentNoderanslationMatrix = SCNMatrix4MakeTranslation((self.yourNode.parent?.worldPosition.x)!, (self.yourNode.parent?.worldPosition.y)!, (self.yourNode.parent?.worldPosition.z)!)
        let parentNodeMatWOTrans = SCNMatrix4Mult((self.yourNode.parent?.worldTransform)!, SCNMatrix4Invert(parentNoderanslationMatrix))
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, parentNodeMatWOTrans)
        let camorbitNodeTransMat = SCNMatrix4MakeTranslation((self.arSceneView.pointOfView?.worldPosition.x)!, (self.arSceneView.pointOfView?.worldPosition.y)!, (self.arSceneView.pointOfView?.worldPosition.z)!)
        let camorbitNodeMatWOTrans = SCNMatrix4Mult((self.arSceneView.pointOfView?.worldTransform)!, SCNMatrix4Invert(camorbitNodeTransMat))
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, SCNMatrix4Invert(camorbitNodeMatWOTrans))
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, rotMatrix)
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, camorbitNodeMatWOTrans)
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, SCNMatrix4Invert(parentNodeMatWOTrans))
        self.yourNode.transform = SCNMatrix4Mult(self.yourNode.transform, transMatrix)
    }
}
...