Search code examples
swiftrotationscenekitaugmented-realityarkit

ARKit cannot rotate a SCNNode correctly on a vertical plane


I want to rotate a SCNNode, which is a painting image.

I can rotate it on the floor, but I cannot rotate it correctly on wall.

(I am using UIRotationGestureRecognizer)

...
  func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {

    ...

    // Add the painting
    let newPaintingNode = SCNNode(geometry: planeGeometry)
    newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
    newPaintingNode.eulerAngles = SCNVector3(newPaintingNode.eulerAngles.x + (-Float.pi / 2), newPaintingNode.eulerAngles.y, newPaintingNode.eulerAngles.z)
    newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
    self.paintingNode = newPaintingNode

    self.currentAngleY = newPaintingNode.eulerAngles.y
    self.currentAngleZ = newPaintingNode.eulerAngles.z

    augmentedRealityView.scene.rootNode.addChildNode(self.paintingNode!)
    grid.removeFromParentNode()
  }
...

  @objc func rotateNode(_ gesture: UIRotationGestureRecognizer){

    if let currentNode = self.paintingNode {
      //1. Get The Current Rotation From The Gesture
      let rotation = Float(gesture.rotation)

      if self.paintingAlignment == "horizontal" {

        log.verbose("rotate horizontal!")

        //2. If The Gesture State Has Changed Set The Nodes EulerAngles.y
        if gesture.state == .changed {
          currentNode.eulerAngles.y = currentAngleY + rotation
        }

        //3. If The Gesture Has Ended Store The Last Angle Of The Cube
        if(gesture.state == .ended) {
          currentAngleY = currentNode.eulerAngles.y
        }

      } else if self.paintingAlignment == "vertical" {

        log.verbose("rotate vertical!")

        //2. If The Gesture State Has Changed Set The Nodes EulerAngles.z
        if gesture.state == .changed {
          currentNode.eulerAngles.z = currentAngleZ + rotation
        }

        //3. If The Gesture Has Ended Store The Last Angle Of The Cube
        if(gesture.state == .ended) {
          currentAngleZ = currentNode.eulerAngles.z
        }
      }
    }
  }

enter image description here

Does anyone know how can I rotate it correctly on wall? Thank you!


Solution

  • I tried SCNNode eulerAngles and SCNNode rotation and I have no luck...

    I guess that is because I invoked SCNNode.transform() when I add the painting. When later I modify SCNNode.eulerAngles and invoke SCNNode.rotation(), they may have influence on the first transform.

    I finally get it working using SCNMatrix4Rotate, not using eulerAngles and rotation.

      func addPainting(_ hitResult: ARHitTestResult, _ grid: Grid) {
        ...
        let newPaintingNode = SCNNode(geometry: planeGeometry)
        newPaintingNode.transform = SCNMatrix4(hitResult.anchor!.transform)
        newPaintingNode.transform = SCNMatrix4Rotate(newPaintingNode.transform, -Float.pi / 2.0, 1.0, 0.0, 0.0)
        newPaintingNode.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
        self.paintingNode = newPaintingNode
        ...
      }
    ...
      @objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
    
        if let _ = self.paintingNode {
    
          // Get The Current Rotation From The Gesture
          let rotation = Float(gesture.rotation)
    
          if gesture.state == .began {
            self.rotationZ = rotation
          }
    
          if gesture.state == .changed {
            let diff = rotation - self.rotationZ
            self.paintingNode?.transform = SCNMatrix4Rotate(self.paintingNode!.transform, diff, 0.0, 0.0, 1.0)
            self.rotationZ = rotation
          }
    
        }
    
      }
    

    And the above code works on both vertical and horizontal plane.