Advertisement
Guest User

Untitled

a guest
Mar 20th, 2019
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 7.60 KB | None | 0 0
  1. import UIKit
  2. import SceneKit
  3. import ARKit
  4.  
  5. class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
  6.  
  7.     @IBOutlet var sceneView: ARSCNView!
  8.     @IBOutlet weak var screenGazeLabel: UILabel!
  9.    
  10.     var faceNode: SCNNode = SCNNode()
  11.    
  12.     var eyeLNode: SCNNode = {
  13.         let geometry = SCNCone(topRadius: 0.005, bottomRadius: 0, height: 0.2)
  14.         geometry.radialSegmentCount = 3
  15.         geometry.firstMaterial?.diffuse.contents = UIColor.blue
  16.         let node = SCNNode()
  17.         node.geometry = geometry
  18.         node.eulerAngles.x = -.pi / 2
  19.         node.position.z = 0.1
  20.         let parentNode = SCNNode()
  21.         parentNode.addChildNode(node)
  22.         return parentNode
  23.     }()
  24.    
  25.     var eyeRNode: SCNNode = {
  26.         let geometry = SCNCone(topRadius: 0.005, bottomRadius: 0, height: 0.2)
  27.         geometry.radialSegmentCount = 3
  28.         geometry.firstMaterial?.diffuse.contents = UIColor.blue
  29.         let node = SCNNode()
  30.         node.geometry = geometry
  31.         node.eulerAngles.x = -.pi / 2
  32.         node.position.z = 0.1
  33.         let parentNode = SCNNode()
  34.         parentNode.addChildNode(node)
  35.         return parentNode
  36.     }()
  37.    
  38.     var lookAtTargetEyeLNode: SCNNode = SCNNode()
  39.     var lookAtTargetEyeRNode: SCNNode = SCNNode()
  40.    
  41.     // actual physical size of iPhoneX screen
  42.     let phoneScreenSize = CGSize(width: 0.0623908297, height: 0.135096943231532)
  43.    
  44.     // actual point size of iPhoneX screen
  45.     let phoneScreenPointSize = CGSize(width: 375, height: 812)
  46.    
  47.     var virtualPhoneNode: SCNNode = SCNNode()
  48.    
  49.     var virtualScreenNode: SCNNode = {
  50.        
  51.         let screenGeometry = SCNPlane(width: 1, height: 1)
  52.         screenGeometry.firstMaterial?.isDoubleSided = true
  53.         screenGeometry.firstMaterial?.diffuse.contents = UIColor.green
  54.        
  55.         return SCNNode(geometry: screenGeometry)
  56.     }()
  57.    
  58.     var eyeLookAtPositionXs: [CGFloat] = []
  59.    
  60.     var eyeLookAtPositionYs: [CGFloat] = []
  61.    
  62.     override var preferredStatusBarStyle: UIStatusBarStyle {
  63.         return UIStatusBarStyle.lightContent
  64.     }
  65.    
  66.     override func viewDidLoad() {
  67.         super.viewDidLoad()
  68.        
  69.        
  70.        
  71.         sceneView.layer.cornerRadius = 28
  72.        
  73.         // Set the view's delegate
  74.         sceneView.delegate = self
  75.         sceneView.session.delegate = self
  76.         sceneView.automaticallyUpdatesLighting = true
  77.        
  78.         // Setup Scenegraph
  79.         sceneView.scene.rootNode.addChildNode(faceNode)
  80.         sceneView.scene.rootNode.addChildNode(virtualPhoneNode)
  81.         virtualPhoneNode.addChildNode(virtualScreenNode)
  82.         faceNode.addChildNode(eyeLNode)
  83.         faceNode.addChildNode(eyeRNode)
  84.         eyeLNode.addChildNode(lookAtTargetEyeLNode)
  85.         eyeRNode.addChildNode(lookAtTargetEyeRNode)
  86.        
  87.         // Set LookAtTargetEye at 2 meters away from the center of eyeballs to create segment vector
  88.         lookAtTargetEyeLNode.position.z = 2
  89.         lookAtTargetEyeRNode.position.z = 2
  90.     }
  91.    
  92.     override func viewWillAppear(_ animated: Bool) {
  93.         super.viewWillAppear(animated)
  94.        
  95.         // Create a session configuration
  96.         guard ARFaceTrackingConfiguration.isSupported else { return }
  97.         let configuration = ARFaceTrackingConfiguration()
  98.         configuration.isLightEstimationEnabled = true
  99.        
  100.         // Run the view's session
  101.         sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
  102.     }
  103.    
  104.     override func viewWillDisappear(_ animated: Bool) {
  105.         super.viewWillDisappear(animated)
  106.        
  107.         // Pause the view's session
  108.         sceneView.session.pause()
  109.     }
  110.  
  111.     // MARK: - ARSCNViewDelegate
  112.    
  113.     func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
  114.        
  115.         faceNode.transform = node.transform
  116.         guard let faceAnchor = anchor as? ARFaceAnchor else { return }
  117.        
  118.         update(withFaceAnchor: faceAnchor)
  119.     }
  120.    
  121.     // MARK: - update(ARFaceAnchor)
  122.    
  123.     func update(withFaceAnchor anchor: ARFaceAnchor) {
  124.        
  125.         eyeRNode.simdTransform = anchor.rightEyeTransform
  126.         eyeLNode.simdTransform = anchor.leftEyeTransform
  127.        
  128.         var eyeLLookAt = CGPoint()
  129.         var eyeRLookAt = CGPoint()
  130.        
  131.         let heightCompensation: CGFloat = 312
  132.        
  133.         DispatchQueue.main.async {
  134.  
  135.             // Perform Hit test using the ray segments that are drawn by the center of the eyeballs to somewhere two meters away at direction of where users look at to the virtual plane that place at the same orientation of the phone screen
  136.            
  137.             let phoneScreenEyeRHitTestResults = self.virtualPhoneNode.hitTestWithSegment(from: self.lookAtTargetEyeRNode.worldPosition, to: self.eyeRNode.worldPosition, options: nil)
  138.            
  139.             let phoneScreenEyeLHitTestResults = self.virtualPhoneNode.hitTestWithSegment(from: self.lookAtTargetEyeLNode.worldPosition, to: self.eyeLNode.worldPosition, options: nil)
  140.            
  141.             for result in phoneScreenEyeRHitTestResults {
  142.                
  143.                 eyeRLookAt.x = CGFloat(result.localCoordinates.x) / (self.phoneScreenSize.width / 2) * self.phoneScreenPointSize.width
  144.                
  145.                 eyeRLookAt.y = CGFloat(result.localCoordinates.y) / (self.phoneScreenSize.height / 2) * self.phoneScreenPointSize.height + heightCompensation
  146.             }
  147.            
  148.             for result in phoneScreenEyeLHitTestResults {
  149.                
  150.                 eyeLLookAt.x = CGFloat(result.localCoordinates.x) / (self.phoneScreenSize.width / 2) * self.phoneScreenPointSize.width
  151.                
  152.                 eyeLLookAt.y = CGFloat(result.localCoordinates.y) / (self.phoneScreenSize.height / 2) * self.phoneScreenPointSize.height + heightCompensation
  153.             }
  154.            
  155.             // Add the latest position and keep up to 8 recent position to smooth with.
  156.             let smoothThresholdNumber: Int = 10
  157.             self.eyeLookAtPositionXs.append((eyeRLookAt.x + eyeLLookAt.x) / 2)
  158.             self.eyeLookAtPositionYs.append(-(eyeRLookAt.y + eyeLLookAt.y) / 2)
  159.             self.eyeLookAtPositionXs = Array(self.eyeLookAtPositionXs.suffix(smoothThresholdNumber))
  160.             self.eyeLookAtPositionYs = Array(self.eyeLookAtPositionYs.suffix(smoothThresholdNumber))
  161.            
  162.             let smoothEyeLookAtPositionX = self.eyeLookAtPositionXs.average!
  163.             let smoothEyeLookAtPositionY = self.eyeLookAtPositionYs.average!
  164.  
  165.            
  166.             // update eye look at labels values
  167.             let positionX = Int(round(smoothEyeLookAtPositionX + self.phoneScreenPointSize.width / 2))
  168.             let positionY = Int(round(smoothEyeLookAtPositionY + self.phoneScreenPointSize.height / 2))
  169.  
  170.             // Checking if user looking at screen
  171.             var screenGaze = true
  172.            
  173.             if -100 ... 500 ~= positionX && -700 ... 1000 ~= positionY {
  174.                 screenGaze = true
  175.             }
  176.             else {
  177.                 screenGaze = false
  178.             }
  179.             self.screenGazeLabel.text = "\(screenGaze)"
  180.         }
  181.        
  182.     }
  183.    
  184.     func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
  185.         virtualPhoneNode.transform = (sceneView.pointOfView?.transform)!
  186.     }
  187.    
  188.     func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
  189.         faceNode.transform = node.transform
  190.         guard let faceAnchor = anchor as? ARFaceAnchor else { return }
  191.         update(withFaceAnchor: faceAnchor)
  192.     }
  193. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement