0

My goal is to be able to move/rotate AR objects using the gestureRecognizer. While I got it working for a single AR cube, I cannot get it work for multiple cubes/objects.

Main part of the viewDidLoad:

        let boxNode1 = addCube(position: SCNVector3(0,0,0), name: "box")
        let boxNode2 = addCube(position: SCNVector3(0,-0.1,-0.1), name: "box2")

        sceneView.scene.rootNode.addChildNode(boxNode1)
        sceneView.scene.rootNode.addChildNode(boxNode2)
        
        var nodes: [SCNNode] = getMyNodes()

        var parentNode = SCNNode()
        parentNode.name = "motherNode"

        for node in nodes {
            parentNode.addChildNode(node)
        }
        sceneView.scene.rootNode.addChildNode(parentNode)

//        sceneView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:))))
        sceneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(ViewController.handleMove(_:))))
        sceneView.addGestureRecognizer(UIRotationGestureRecognizer(target: self, action: #selector(ViewController.handleRotate(_:))))
        
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)

Part of panGesture (works for each cube, but does not work if I change to nodeHit.Parent!) The parent node is detected correctly, but no change is made to it:

    @objc func handleMove(_ gesture: UIPanGestureRecognizer) {

    //1. Get The Current Touch Point
    let location = gesture.location(in: self.sceneView)

    //2. Get The Next Feature Point Etc
    guard let nodeHitTest = self.sceneView.hitTest(location, options: nil).first else { print("no node"); return }

    var nodeHit = nodeHitTest.node

//    nodeHit = nodeHit.parent!
    //3. Convert To World Coordinates
    let worldTransform = nodeHitTest.simdWorldCoordinates
    //4. Apply To The Node
        nodeHit.position = SCNVector3(worldTransform.x, worldTransform.y, 0)
        
    }

What I want to do is to be able to move both cube at once (so they all undergoes the same translation). It sounds possible from this post: How to join multiple nodes to one node in iOS scene However, at the same time this post also says I cannot do that for reason I do not understand yet: SceneKit nodes aren't changing position with scene's root node In the worst case I think it is possible to manually apply the transformation to every child node, however applying translation to one parent Node seems to be a much elegant way for doing this.

Edit: I tried this way and can get both moving nodes moving, however sometimes the position is reversed (sometimes the cube comes on top of the other when they should not):

    @objc func handleMove(_ gesture: UIPanGestureRecognizer) {

    //1. Get The Current Touch Point
    let location = gesture.location(in: self.sceneView)

    //2. Get The Next Feature Point Etc
    guard let nodeHitTest = self.sceneView.hitTest(location, options: nil).first else { print("no node"); return }

//    var nodeHit = nodeHitTest.node

    let nodeHit = nodeHitTest.node
    let original_x = nodeHitTest.node.position.x
    let original_y = nodeHitTest.node.position.y
    print(original_x, original_y)
//    let nodeHit = sceneView.scene.rootNode.childNode(withName: "motherNode2", recursively: true)
    //3. Convert To World Coordinates
    let worldTransform = nodeHitTest.simdWorldCoordinates
    //4. Apply To The Node
////    nodeHit.position = SCNVector3(worldTransform.x, worldTransform.y, 0)
    nodeHit.position = SCNVector3(worldTransform.x, worldTransform.y, 0)

    for node in nodeHit.parent!.childNodes {
        if node.name != nil{
            if node.name != nodeHit.name {
                let old_x = node.position.x
                let old_y = node.position.y
                print(old_x, old_y)
                node.position = SCNVector3((nodeHit.simdPosition.x + original_x - old_x), (nodeHit.simdPosition.y + original_y - old_y), 0)
            }
        }
    }

Any ideas?

tonywang
  • 181
  • 2
  • 13

1 Answers1

0

I swapped the plus minus sign and now it is working correctly. Here is the code:

    /// - Parameter gesture: UIPanGestureRecognizer
    @objc func handleMove(_ gesture: UIPanGestureRecognizer) {

    //1. Get The Current Touch Point
    let location = gesture.location(in: self.sceneView)

    //2. Get The Next Feature Point Etc
    guard let nodeHitTest = self.sceneView.hitTest(location, options: nil).first else { print("no node"); return }

//    var nodeHit = nodeHitTest.node

    let nodeHit = nodeHitTest.node
    let original_x = nodeHitTest.node.position.x
    let original_y = nodeHitTest.node.position.y
//    let nodeHit = sceneView.scene.rootNode.childNode(withName: "motherNode2", recursively: true)
    //3. Convert To World Coordinates
    let worldTransform = nodeHitTest.simdWorldCoordinates
    //4. Apply To The Node
////    nodeHit.position = SCNVector3(worldTransform.x, worldTransform.y, 0)
    nodeHit.position = SCNVector3(worldTransform.x, worldTransform.y, 0)

    for node in nodeHit.parent!.childNodes {
        if node.name != nil{
            if node.name != nodeHit.name {
                let old_x = node.position.x
                let old_y = node.position.y
                node.position = SCNVector3((nodeHit.simdPosition.x - original_x + old_x), (nodeHit.simdPosition.y - original_y + old_y), 0)
            }
        }
    }

The idea is even without grouping everything into a new node, I can access all the nodes using nodeHit.parent!.childNodes. This also contains other nodes created by default such as camera or light source so I added the condition to make sure it only selects the nodes with the names I have created. Ideally you just need to use some built in methods to move all the nodes in the scene but I cannot find such method.

So my method first keep track of the old position before moving, then if it is node hit by hit test, move it as before. However, if it is not the node hit by hit test, reposition it by the difference between the the 2 nodes. The relative position difference should be the same regardless of where you move the nodes.

tonywang
  • 181
  • 2
  • 13