4
votes

Thanks for taking a look at this. I'm sure it is something very basic. I am a beginner.

I am trying to rotate a cube in in a SceneKit view with pan gestures. I have so far been successful in load this sample app onto my iPad and panning my finger on the y axis to rotate the cube on its x axis, or panning along the screens x-axis to rotate the cube along its y-axis.

Currently, I've noticed that whichever gesture recognizer is added last to the sceneView is the one that works. My question is how can I have the cube respond to either a x pan gesture then a y pan gesture or vice versa.

Here is the code I have written so far:

import UIKit
import SceneKit

class ViewController: UIViewController {

  //geometry
  var geometryNode: SCNNode = SCNNode()

  //gestures
  var currentYAngle: Float = 0.0
  var currentXAngle: Float = 0.0

  override func viewDidLoad() {
    super.viewDidLoad()

    sceneSetup()  
  }

  func sceneSetup () {
    //setup scene
    let scene = SCNScene()
    let sceneView = SCNView(frame: self.view.frame)
    self.view.addSubview(sceneView)

    //add camera
    let camera = SCNCamera()
    let cameraNode = SCNNode()
    cameraNode.camera = camera
    cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 5.0)

    //add light
    let light = SCNLight()
    light.type = SCNLightTypeOmni
    let lightNode = SCNNode()
    lightNode.light = light
    lightNode.position = SCNVector3(x: 1.5, y: 1.5, z: 1.5)

    //add cube
    let cubeGeometry = SCNBox(width: 1.0, height: 1.0, length: 1.0, chamferRadius: 0.0)
    let boxNode = SCNNode(geometry: cubeGeometry)

    scene.rootNode.addChildNode(lightNode)
    scene.rootNode.addChildNode(cameraNode)
    scene.rootNode.addChildNode(boxNode)

    geometryNode = boxNode

    //add recognizers
    let panXRecognizer = UIPanGestureRecognizer(target: self, action: "rotateXGesture:")
    let panYRecognizer = UIPanGestureRecognizer(target: self, action: "rotateYGesture:")
    sceneView.addGestureRecognizer(panXRecognizer)
    sceneView.addGestureRecognizer(panYRecognizer)

    sceneView.scene = scene
  }

  func rotateXGesture (sender: UIPanGestureRecognizer) {
    let translation = sender.translationInView(sender.view)
    var newXAngle = (Float)(translation.y)*(Float)(M_PI)/180.0
    newXAngle += currentXAngle

    geometryNode.transform = SCNMatrix4MakeRotation(newXAngle, 1, 0, 0)

    if(sender.state == UIGestureRecognizerState.Ended) {
      currentXAngle = newXAngle
    }
  }

  func rotateYGesture (sender: UIPanGestureRecognizer) {
    let translation = sender.translationInView(sender.view)
    var newYAngle = (Float)(translation.x)*(Float)(M_PI)/180.0
    newYAngle += currentYAngle

    geometryNode.transform = SCNMatrix4MakeRotation(newYAngle, 0, 1, 0)

    if(sender.state == UIGestureRecognizerState.Ended) {
      currentYAngle = newYAngle
    }
  }
}
2

2 Answers

9
votes

Combine your current two gestures into one. Here's the relevant portion of the code I'm using:

func panGesture(sender: UIPanGestureRecognizer) {
    let translation = sender.translationInView(sender.view!)

    var newAngleX = (Float)(translation.y)*(Float)(M_PI)/180.0
    newAngleX += currentAngleX
    var newAngleY = (Float)(translation.x)*(Float)(M_PI)/180.0
    newAngleY += currentAngleY

    baseNode.eulerAngles.x = newAngleX
    baseNode.eulerAngles.y = newAngleY

    if(sender.state == UIGestureRecognizerState.Ended) {
        currentAngleX = newAngleX
        currentAngleY = newAngleY
    }
}

Here's a gesture for zooming as well:

func pinchGesture(sender: UIPinchGestureRecognizer) {
    let zoom = sender.scale
    var z = cameraNode.position.z  * Float(1.0 / zoom)
    z = fmaxf(zoomLimits.min, z)
    z = fminf(zoomLimits.max, z)

    cameraNode.position.z = z
}

Edit: I found a better way to rotate the model. In the panGesture code at the top, the x-axis pivots as you rotate about the y. This means if you rotate 180 about the y, rotation about the x is opposite your finger motion. The method also restricts motion to two degrees of freedom. The method linked to below, even though it doesn't directly affect the z-axis, somehow seems to allow three degrees of freedom. It also makes all vertical swipes rotate about the x in the logical direction. How to rotate object in a scene with pan gesture - SceneKit

1
votes

The way you set up your two gesture recognizers is identical, they will both fire for the same events (which is why the last one to be added predominates). There is no control within pan to specifically limit it to vertical or horizontal pans. Instead, consider analyzing the direction of the pan and then decide whether to rotate your gesture one way or the other, based upon which is greater.