如何使用ARKit和Gesture Recognizer移动和旋转SCNode?

时间:2018-05-22 07:04:46

标签: swift4 scenekit arkit ios11.3

我正在使用ARKit(SceneKit)开发基于AR的iOS应用程序。我使用Apple示例代码https://developer.apple.com/documentation/arkit/handling_3d_interaction_and_ui_controls_in_augmented_reality作为基础。使用此我可以移动或旋转整个虚拟对象。

但我想使用用户手指在虚拟对象中选择和移动/旋转子节点,类似于我们移动/旋转整个虚拟对象本身的方式。

我尝试了以下两个链接,但它只是在特定轴上移动子节点,但在用户移动手指时不能随意移动。

ARKit - Drag a node along a specific axis (not on a plane)

Dragging SCNNode in ARKit Using SceneKit

此外,我尝试使用SCNode替换作为SCNReferenceNode的虚拟对象,以便现有虚拟对象的任何功能也适用于子节点,但它不起作用。

任何人都可以帮助我如何自由移动/旋转虚拟对象以及虚拟对象的子节点?

请在下面找到我目前正在使用的代码,

       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 :(得分:2)

移动物体:

执行hitTest检查您触摸的位置,并检测您触摸的平面并获得位置。通过使用SCNVector3更改node.position值将SCNNode移动到该位置。

代码:

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