4

Assume a SceneKit scene offers a perspective similar to Minecraft. There is one camera. The camera node acts like the "head" of a person while its parent node acts like the "body." The camera node is a child of the parent node, called "userNode."

The goal:

  • Let the user pan with one finger to rotate the camera. In other words, the camera position remains fixed, but the "head" rotates around and lets the user view the scene as if turning around in a circle.

  • Let the user pan with two fingers to orbit the camera around a given point. In other words, the camera position and rotation now change in order to let the user view the same point from different angles.

So far, the code below implements the one-finger pan behavior. The question is how to incorporate the two-finger pan behavior.

Option 1: Follow this answer, which explains how to orbit a camera. This seems suboptimal, however, since there is already a camera node in the scene and requires additional nodes while also shifting the scene's point of view between different cameras.

Option 2: Compute the new position and rotation of the camera, which lets you avoid adding another camera node and another orbit node. This feels like the better answer but may be more complex.

func sceneViewPannedTwoFingers(sender: UIPanGestureRecognizer) {
    // Get pan distance & convert to radians
    let translation = sender.translationInView(sender.view!)
    var xRadians = GLKMathDegreesToRadians(Float(translation.x))

    // Get X radians only since only moving horizontally
    xRadians = (xRadians / 10) + curXRadians

    // Rotate & move camera horizontally
    userNode.rotation = SCNVector4(x: 0, y: 1, z: 0, w: -xRadians)

    // Compute new position for camera?
}


func sceneViewPannedOneFinger(sender: UIPanGestureRecognizer) {
    // Get pan distance & convert to radians
    let translation = sender.translationInView(sender.view!)
    var xRadians = GLKMathDegreesToRadians(Float(translation.x))
    var yRadians = GLKMathDegreesToRadians(Float(translation.y))

    // Get x & y radians
    xRadians = (xRadians / 10) + curXRadians
    yRadians = (yRadians / 10) + curYRadians

    // Limit yRadians to prevent rotating 360 degrees vertically
    yRadians = max(Float(-M_PI_2), min(Float(M_PI_2), yRadians))

    // Set rotation values to avoid Gimbal Lock
    cameraNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: yRadians)
    userNode.rotation = SCNVector4(x: 0, y: 1, z: 0, w: xRadians)

    // Save value for next rotation
    if sender.state == UIGestureRecognizerState.Ended {
        curXRadians = xRadians
        curYRadians = yRadians
    }

    // Set preview block
    setPreviewBlock()
}

Questions:

1) Is there an easier way to accomplish this in SceneKit?

2) If not, are the two options above the only two options, and if so, which is the better option to choose?

3) For option 2, updating the position of userNode, or the "body", seems simple enough: just compute the new position along a circle given the center's origin, i.e., the orbit point). For the "head", is there anything else to be mindful beyond updating the rotation property of cameraNode (or the "head")? It seems like orienting the camera to always face the orbit point might be more complex since there is a part of the SceneKit API dedicated to it, e.g., SCNLookAtConstraint.

Community
  • 1
  • 1
Crashalot
  • 33,605
  • 61
  • 269
  • 439

0 Answers0