1

I am working on an AR based iOS app using ARKit(SceneKit). I used the Apple sample code https://developer.apple.com/documentation/arkit/handling_3d_interaction_and_ui_controls_in_augmented_reality as base for this. Using this i am able to move or rotate the whole Virtual Object.

But i want to select and move/rotate a Child Node in Virtual object using user finger, similar to how we move/rotate the whole Virtual Object itself.

I tried the below two links but it is only moving the child node in particular axis but not freely moving anywhere as the user moves the finger.

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

Dragging SCNNode in ARKit Using SceneKit

Also i tried replacing the Virtual Object which is a SCNReferenceNode with SCNode so that whatever functionality present for existing Virtual Object applies to Child Node as well, it is not working.

Can anyone please help me on how to freely move/rotate not only the Virtual Object but also the child node of a Virtual Object?

Please find the code i am currently using below,

       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)
        }
Vidhya Sri
  • 1,773
  • 1
  • 17
  • 46

1 Answers1

3

To move an object:

Perform a hitTest to check where you have touched, and detect which plane you touched and get a position. Move your SCNNode to that position by changing the node.position value with an SCNVector3.

Code:

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

The above code is enough to move your node over a detected plane, anywhere you touch, and not just in a single axis.

Rotating a node according to your gesture is a very difficult task and I have worked on a solution for quite sometime, never reaching a perfect output. But, I have come across this repository in GitHub which allows you to do just that with a very impressive result. https://github.com/Xartec/ScreenSpaceRotationAndPan

The Swift version of the code you require to rotate your node using your gesture would be :

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)
    }
}
Aayushi
  • 787
  • 10
  • 14
SWAT
  • 1,107
  • 8
  • 19
  • This doesn't cause smooth movement of my node. As soon as i touch the node, it just went somewhere else. It doesn't follow my finger movement. – Vidhya Sri May 24 '18 at 07:48
  • Explain your trouble a little more. that would help me get to the cause of the trouble. I have assumed that you have plane detection enabled in your configuration. – SWAT May 24 '18 at 07:54
  • Yes horizontal plane detection is enabled in my Configuration. What i am trying to do is, i have a whole Virtual Object placed in Surface. It has lot of child nodes in it. I want to move each child node with finger and should be able to rotate it as well. Now when i try this code, when i touch a child node it is disappearing from its place and i can see it is floating somewhere farther in screen. – Vidhya Sri May 24 '18 at 08:09
  • The code I gave you is not meant to separate the child node and move it separately from the parentNode, it would move the nodes together. I will add the code to separate the child node, but please share your code. It would help a lot. – SWAT May 24 '18 at 09:08
  • I have edited my question. Please find the code i am trying. – Vidhya Sri May 24 '18 at 09:42