Skip to content

Instantly share code, notes, and snippets.

@digitallysavvy
Last active December 1, 2019 06:04
Show Gist options
  • Save digitallysavvy/d78783f8d886d20d26356d70c386cb1b to your computer and use it in GitHub Desktop.
Save digitallysavvy/d78783f8d886d20d26356d70c386cb1b to your computer and use it in GitHub Desktop.
A simple ARSCN View Controller with full render delegate list.
//
// ARViewController.swift
//
// Created by digitallysavvy.
// Copyright © 2019 digitallysavvy All rights reserved.
//
import UIKit
import ARKit
internal class ARViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
var sceneView : ARSCNView!
var scnLights : [SCNNode] = []
let debug : Bool = true // toggle the debug logs
// MARK: VC Events
override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor.black // set the background color
// Setup sceneview
let sceneView = ARSCNView() //instantiate scene view
self.view.insertSubview(sceneView, at: 0)
//add sceneView layout contstraints
sceneView.translatesAutoresizingMaskIntoConstraints = false
sceneView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
sceneView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
sceneView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
sceneView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
// set reference to sceneView
self.sceneView = sceneView
}
override func viewDidLoad() {
super.viewDidLoad()
// set ARKit render and session delegates
self.sceneView.delegate = self
self.sceneView.session.delegate = self
if debug {
self.sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, ARSCNDebugOptions.showFeaturePoints, .showBoundingBoxes]
self.sceneView.showsStatistics = true
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Configure ARKit Session
let configuration = ARWorldTrackingConfiguration() // ARKit trackign configuration
configuration.planeDetection = [.horizontal, .vertical]
configuration.providesAudioData = true // enable audio data
configuration.isLightEstimationEnabled = true // enable light estimation (save on processing)
self.sceneView.session.run(configuration) // set cunfiguration for ARKit session
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let light = self.createLight(withPosition: SCNVector3(x: 0,y: 5,z: 0), andEulerRotation: SCNVector3(-Float.pi / 2, 0, 0))
self.sceneView.scene.rootNode.addChildNode(light)
self.scnLights.append(light)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
self.sceneView.session.pause()
self.sceneView.removeFromSuperview()
self.sceneView = nil
}
// MARK: Hide status bar
override var prefersStatusBarHidden: Bool {
return true
}
// MARK: Render delegate
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
guard let currentFrame = self.sceneView.session.currentFrame else { return }
let intensity : CGFloat = currentFrame.lightEstimate!.ambientIntensity / 1000.0
self.sceneView.scene.lightingEnvironment.intensity = intensity
if scnLights.count > 0 {
for node in scnLights {
node.light?.intensity = intensity
}
}
// do something when scene will render
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
// do something on render update
}
func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) {
}
// plane detection
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// anchor plane detection
}
// plane updating
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// anchor plane is updated
}
func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {
// anchor plane is removed
}
// MARK: Session delegate
func session(_ session: ARSession, didUpdate frame: ARFrame) {
// ARSession did update
}
func session(_ session: ARSession, didOutputAudioSampleBuffer audioSampleBuffer: CMSampleBuffer) {
// ARSession did output audio data
}
func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
// ARSession camera tracking state change
}
func sessionWasInterrupted(_ session: ARSession) {
// ARSession was interrupted
}
func sessionInterruptionEnded(_ session: ARSession) {
// ARSession interuption ended
}
func sessionShouldAttemptRelocalization(_ session: ARSession) -> Bool {
// whether to attempt recovery of world-tracking state after an interruption.
return true
}
// MARK: Lights
func createLight(withPosition position: SCNVector3, andEulerRotation rotation: SCNVector3) -> SCNNode {
// Create a directional light node with shadow
let directionalNode : SCNNode = SCNNode()
directionalNode.light = SCNLight()
directionalNode.light?.type = SCNLight.LightType.directional
directionalNode.light?.color = UIColor.white
directionalNode.light?.castsShadow = true
directionalNode.light?.automaticallyAdjustsShadowProjection = true
directionalNode.light?.shadowSampleCount = 64
directionalNode.light?.shadowRadius = 16
directionalNode.light?.shadowMode = .deferred
directionalNode.light?.shadowMapSize = CGSize(width: 1024, height: 1024)
directionalNode.light?.shadowColor = UIColor.black.withAlphaComponent(0.5)
directionalNode.position = position
directionalNode.eulerAngles = rotation
return directionalNode
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment