Skip to content

Instantly share code, notes, and snippets.

@kylehowells
Last active December 8, 2022 17:45
Show Gist options
  • Save kylehowells/6cee5ee5cb016d6cf08fecd305f767fe to your computer and use it in GitHub Desktop.
Save kylehowells/6cee5ee5cb016d6cf08fecd305f767fe to your computer and use it in GitHub Desktop.

Revisions

  1. kylehowells revised this gist Dec 8, 2022. No changes.
  2. kylehowells revised this gist Dec 8, 2022. No changes.
  3. kylehowells created this gist Dec 8, 2022.
    144 changes: 144 additions & 0 deletions SceneKitBoundingBox.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    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
    })
    }