The code below is meant to simulate the default camera controls in SceneKit, specifically the zoom and rotate functionality.
However, the code isn't nearly as smooth and versatile as the default controls.
For instance, if you load these two models, the default zoom works consistently for both models. The code below only works well, however, for model 1 but zooms far too fast for model 2.
Any help would be appreciated.
Model 1: https://poly.google.com/view/6mRHqTCZHxw
Model 2: https://poly.google.com/view/cKryD9VnDEZ
// Create camera
let camera = SCNCamera()
camera.automaticallyAdjustsZRange = true
camera.xFov = 30
camera.yFov = 0
// Compute distance to place camera
let theta = GLKMathDegreesToRadians(Float(camera.xFov/2))
let adjacentLength = oppositeLength / Float(tan(theta))
// Add camera to <cameraNode>
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: adjacentLength)
// Add <cameraNode> to <orbitNode>
orbitNode.addChildNode(cameraNode)
// Add <orbitNode>
scene.rootNode.addChildNode(orbitNode)
// Handle pinches
let pinchRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(didSceneViewPinch))
pinchRecognizer.delegate = self
sceneView.addGestureRecognizer(pinchRecognizer)
// Handle pans
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didSceneViewPanOneFinger))
panRecognizer.minimumNumberOfTouches = 1
panRecognizer.maximumNumberOfTouches = 1
panRecognizer.delegate = self
sceneView.addGestureRecognizer(panRecognizer)
func rotate(recognizer: UIPanGestureRecognizer) {
// Set throttle value to slow down rotation
let throttleFactor = Float(40)
// Compute rotation
let translation = recognizer.translation(in: recognizer.view!)
let xRadians = GLKMathDegreesToRadians(Float(translation.x)) / throttleFactor
let yRadians = GLKMathDegreesToRadians(Float(translation.y)) / throttleFactor
// Orbit camera via <orbitNode>
orbitNode.eulerAngles.x -= yRadians
orbitNode.eulerAngles.y -= xRadians
}
func zoom(recognizer: UIPinchGestureRecognizer) {
// Set zoom properties
let minVelocity = CGFloat(0.10)
let zoomDelta = 0.5
// Only zoom when gesture changing and when velocity exceeds <minVelocity>
if recognizer.state == .changed {
// Ignore gesture on tiny movements
if abs(recognizer.velocity) <= minVelocity {
return
}
// If here, zoom in or out based on velocity
let deltaFov = recognizer.velocity > 0 ? -zoomDelta : zoomDelta
var newFov = cameraNode.camera!.xFov + deltaFov
// Make sure FOV remains within min and max values
if newFov <= minXFov {
newFov = minXFov
} else if newFov >= maxXFov {
newFov = maxXFov
}
// Update FOV?
if cameraNode.camera?.xFov != newFov {
cameraNode.camera?.xFov = newFov
}
}
}
I tried recreating SCNCamera rotation as such in default camera controller. But it was not as smooth as the default camera. So I added pan gesture with two minimum touches and left the function empty doing nothing. This makes use of default camera rotation property alone and avoiding below two properties of the default camera as mentioned in https://developer.apple.com/documentation/scenekit/scnview/1523171-allowscameracontrol
1.Pan with two fingers to translate the camera on its local xy-plane 2.Pan with three fingers vertically to move the the camera forward backward
//Pan Gesture
let gesture = UIPanGestureRecognizer(target: self, action: #selector(panDetected(sender:)))
gesture.maximumNumberOfTouches = 3
gesture.minimumNumberOfTouches = 2
self.sceneView.addGestureRecognizer(gesture);
//Doing nothing in pan gesture other than printing
@objc func panDetected (sender: UIPanGestureRecognizer) {
print("two pan detected")
}
Next add pinch gesture to the scene view so that minimum and maximum zoom level can be controlled
//pinch gesture
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinched(sender:)))
self.sceneView.addGestureRecognizer(pinchGesture);
@objc func pinched(sender:UIPinchGestureRecognizer){
let pinchVelocity = Double.init(sender.velocity)
//print("PinchVelocity \(pinchVelocity)")
self.sceneView.pointOfView?.camera!.orthographicScale -= (pinchVelocity/pinchAttenuation)
if (self.sceneView.pointOfView?.camera!.orthographicScale)! <= 0.5 {
self.sceneView.pointOfView!.camera!.orthographicScale = 0.5
}
if self.sceneView.pointOfView!.camera!.orthographicScale >= 1 {
self.sceneView.pointOfView?.camera!.orthographicScale = 1
}
}
Add UIRotationGestureRecognizer incase you want to disable or modify default camera rotation(Rotate with two fingers to roll the camera (rotate on the camera node's z-axis)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With