import SceneKit struct SceneKitBoundingBox { let backLeftTop: SCNVector3 let backLeftBottom: SCNVector3 let backRightTop: SCNVector3 let backRightBottom: SCNVector3 let frontLeftTop: SCNVector3 let frontLeftBottom: SCNVector3 let fromRightTop: SCNVector3 let fromRightBottom: SCNVector3 init(min: SCNVector3, max: SCNVector3) { self.backLeftTop = SCNVector3(min.x, min.y, max.z) self.backLeftBottom = SCNVector3(min.x, min.y, min.z) self.backRightTop = SCNVector3(max.x, min.y, max.z) self.backRightBottom = SCNVector3(max.x, min.y, min.z) self.frontLeftTop = SCNVector3(min.x, max.y, max.z) self.frontLeftBottom = SCNVector3(min.x, max.y, min.z) self.fromRightTop = SCNVector3(max.x, max.y, max.z) self.fromRightBottom = SCNVector3(max.x, max.y, min.z) } } func convertToScreen(coord: SCNVector3, fromNode: SCNNode) -> CGPoint { let converted: SCNVector3 = fromNode.convertPosition(coord, to: nil) let projectedPoint: SCNVector3 = self.sceneView.projectPoint(converted) let point = CGPoint( x: CGFloat(projectedPoint.x), y: CGFloat(projectedPoint.y) ) return point } struct ScreenBoundingBox { let backLeftTop: CGPoint let backLeftBottom: CGPoint let backRightTop: CGPoint let backRightBottom: CGPoint let frontLeftTop: CGPoint let frontLeftBottom: CGPoint let fromRightTop: CGPoint let fromRightBottom: CGPoint init(boundingBox: SceneKitBoundingBox, fromNode: SCNNode, convertToScreen: (SCNVector3, SCNNode) -> CGPoint) { self.backLeftTop = convertToScreen(boundingBox.backLeftTop, fromNode) self.backLeftBottom = convertToScreen(boundingBox.backLeftBottom, fromNode) self.backRightTop = convertToScreen(boundingBox.backRightTop, fromNode) self.backRightBottom = convertToScreen(boundingBox.backRightBottom, fromNode) self.frontLeftTop = convertToScreen(boundingBox.frontLeftTop, fromNode) self.frontLeftBottom = convertToScreen(boundingBox.frontLeftBottom, fromNode) self.fromRightTop = convertToScreen(boundingBox.fromRightTop, fromNode) self.fromRightBottom = convertToScreen(boundingBox.fromRightBottom, fromNode) } private var points: [CGPoint] { return [ self.backLeftTop, self.backLeftBottom, self.backRightTop, self.backRightBottom, self.frontLeftTop, self.frontLeftBottom, self.fromRightTop, self.fromRightBottom, ] } var minX: CGPoint { return self.points.min(by: { $0.x < $1.x })! } var maxX: CGPoint { return self.points.max(by: { $0.x < $1.x })! } var minY: CGPoint { return self.points.min(by: { $0.y < $1.y })! } var maxY: CGPoint { return self.points.max(by: { $0.y < $1.y })! } } private func fixFieldOfView() { guard let scene: SCNScene = self.scene else { return } if let firstChild: SCNNode = scene.rootNode.childNodes.first { let boundingBox = firstChild.boundingBox let fullBoundingBox = SceneKitBoundingBox(min: boundingBox.min, max: boundingBox.max) let screenBoundingBox = ScreenBoundingBox( boundingBox: fullBoundingBox, fromNode: firstChild, convertToScreen: self.convertToScreen(coord:fromNode:) ) let minX: CGFloat = screenBoundingBox.minX.x let maxX: CGFloat = screenBoundingBox.maxX.x let minY: CGFloat = screenBoundingBox.minY.y let maxY: CGFloat = screenBoundingBox.maxY.y print("minX: \(minX) - maxX: \(maxX)") print("minY: \(minY) - maxY: \(maxY)") let totalWidth: CGFloat = (maxX - minX) let scaleX = totalWidth / self.screenWidth print("totalWidth: \(totalWidth) - screenWidth: \(self.screenWidth) - scaleX: \(scaleX)") if let cameraNode = self.sceneView.pointOfView, let camera = cameraNode.camera { let fieldOfView = String(format: "%.3f", camera.fieldOfView) print("camera: \(camera) - fieldOfView: \(fieldOfView) - position: \(cameraNode.position)") if scaleX > 1 { let fieldOfView: CGFloat = camera.fieldOfView let newFieldOfView: CGFloat = fieldOfView / (1.0 / scaleX) camera.fieldOfView = newFieldOfView print("fieldOfView: \(fieldOfView) - newFieldOfView: \(newFieldOfView)") } } } DispatchQueue.main.async(execute: { self.sceneView.alpha = 1 }) }