import SwiftUI import ARKit struct Fonts { static func avenirNextCondensedBold (size: CGFloat) -> Font { return Font.custom("AvenirNextCondensed-Bold", size: size) } static func neutonRegular (size: CGFloat) -> Font { return Font.custom("Neuton-Regular", size: size) } } struct ContentView: View { @State var arview = ARSCNView() var body: some View { VStack { CustomARView(view: arview) } .padding() } } struct CustomARView: UIViewRepresentable { typealias UIViewType = ARSCNView var view:ARSCNView var options: [Any] = [] func makeUIView(context: Context) -> ARSCNView { view.session.delegate = context.coordinator view.scene.background.contents = UIColor.gray return view } func updateUIView(_ view: ARSCNView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self.view) } } class Coordinator: NSObject, ARSCNViewDelegate, ARSessionDelegate { private var trackingView:ARSCNView private var faceGeometry: ARSCNFaceGeometry! private var faceNode: SCNNode! private var spawnTime:TimeInterval = 0 private var cylinderNode: SCNNode! private var coreNode: SCNNode! private var textNodes = [SCNNode](repeating: SCNNode(), count: 9) private var shadowNodes = [SCNNode](repeating: SCNNode(), count: 9) private var coreNodes = [SCNNode](repeating: SCNNode(), count: 9) private var plainNodes = [SCNNode](repeating: SCNNode(), count: 9) private var paperNodes = [SCNNode](repeating: SCNNode(), count: 9) private var colours = [UIColor](repeating: UIColor(), count: 9) private var fmotion = false private var bmotion = false private var doubleLock = true private var prefish = 0 private var postfish = 0 private var previousIndex = 0 private var postIndex = 0 private var currentIndex = 8 { didSet { previousIndex = currentIndex - 1 if previousIndex < 0 { previousIndex = 8 } previousIndex = previousIndex % 9 prefish = previousIndex - 1 if prefish < 0 { prefish = 8 } prefish = prefish % 9 currentIndex = currentIndex % 9 postIndex = currentIndex + 1 postIndex = postIndex % 9 postfish = postIndex + 1 postfish = postfish % 9 } } private var angle = 360.0 / 9 private let directs = ["2Quick","3Brown","4Fox","5Jumps","6Over","7The","8Lazy","9Dog","1The"] private let indirects = ["kciuQ2","nworB3","xoF4","spmuJ5","revO6","ehT7","yzaL8","goD9","ehT1"] private var flip = false init(_ view: ARSCNView) { self.trackingView = view super.init() guard ARFaceTrackingConfiguration.isSupported else { fatalError("Face tracking not available on this on this device model!") } let configuration = ARFaceTrackingConfiguration() self.trackingView.session.run(configuration) self.trackingView.delegate = self let geo = SCNCylinder(radius: 0.12, height: 0.05) geo.radialSegmentCount = 9 cylinderNode = SCNNode(geometry: geo) cylinderNode.geometry?.firstMaterial?.fillMode = .fill cylinderNode.geometry?.firstMaterial?.diffuse.contents = UIColor.gray.withAlphaComponent(1) var geo2 = SCNPlane(width: 0.025, height: 0.08) geo2.firstMaterial?.fillMode = .fill //geo2.firstMaterial?.diffuse.contents = UIColor.blue.withAlphaComponent(0.9) //let newColor = UIColor(red: 255/255, green: 105/255, blue: 255/255, alpha: 0.9) //geo2.firstMaterial?.diffuse.contents = newColor for no in 1...9 { let character = UnicodeScalar(no + 64) // textNodes[no - 1] = newTextNode(Text: "\(no)") let doo = Double(no) * 8.4 + 10 let doo2 = Double(no) * 8.4 + 10 let doo3 = Double(no) * 8.4 + 10 let newColor = UIColor(red: doo/255, green:doo2/255, blue: doo3/255, alpha: 0.8) let fixedColor = UIColor(red: 0.203922, green: 0.203922, blue: 0.203922, alpha: 0.8) //textNodes[no - 1] = newTextNode(Text: String(character!), newColor: fixedColor) colours[no - 1] = UIColor.gray textNodes[no - 1] = newTextNode(Text: directs[no - 1], newColor: fixedColor) shadowNodes[no - 1] = newTextNode(Text: indirects[no - 1], newColor: fixedColor) //textNodes[no - 1].simdPosition.z += 0.1 coreNodes[no - 1] = SCNNode() coreNodes[no - 1].simdEulerAngles.y = GLKMathDegreesToRadians(Float(angle * Double(no))) cylinderNode.addChildNode(coreNodes[no - 1]) //coreNodes[no - 1].addChildNode(textNodes[no - 1]) var geo2 = SCNPlane(width: 0.025, height: 0.08) geo2.firstMaterial?.fillMode = .fill let foo = Double(no) * 4.4 + 80 let foo2 = Double(no) * 6.4 + 80 let foo3 = Double(no) * 8.4 + 80 let newColor2 = UIColor(red: foo/255, green:foo2/255, blue: foo3/255, alpha: 1) geo2.firstMaterial?.diffuse.contents = newColor2 plainNodes[no - 1] = SCNNode(geometry: geo2) paperNodes[no - 1] = SCNNode(geometry: geo2) plainNodes[no - 1].addChildNode(textNodes[no - 1]) paperNodes[no - 1].addChildNode(shadowNodes[no - 1]) paperNodes[no - 1].isHidden = true plainNodes[no - 1].isHidden = false coreNodes[no - 1].addChildNode(plainNodes[no - 1]) coreNodes[no - 1].addChildNode(paperNodes[no - 1]) plainNodes[no - 1].simdPosition.z += 0.14 paperNodes[no - 1].simdPosition.z += 0.14 } // let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) // self.trackingView.addGestureRecognizer(tapGesture) let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:))) swipeLeftGesture.direction = .left self.trackingView.addGestureRecognizer(swipeLeftGesture) let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:))) swipeRightGesture.direction = .right self.trackingView.addGestureRecognizer(swipeRightGesture) } // @objc func handleTap(_ sender: UIGestureRecognizer? = nil) { // let tapLocation = sender?.location(in: self.trackingView) // SCNTransaction.begin() // SCNTransaction.animationDuration = 1 // cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(10) // SCNTransaction.commit() // } @objc func handleSwipe(_ sender: UISwipeGestureRecognizer? = nil) { switch sender?.direction { case UISwipeGestureRecognizer.Direction.right: SCNTransaction.begin() SCNTransaction.animationDuration = 1 cylinderNode.simdEulerAngles.y += GLKMathDegreesToRadians(Float(angle)) currentIndex -= 1 if currentIndex < 0 { currentIndex = 8 } SCNTransaction.commit() print("RIGHT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)") if postfish == 0 { flip.toggle() } if flip { paperNodes[postfish].isHidden = false plainNodes[postfish].isHidden = true } if !flip { paperNodes[postfish].isHidden = true plainNodes[postfish].isHidden = false } case UISwipeGestureRecognizer.Direction.left: print("LEFT prefish \(prefish) previousIndex \(previousIndex) currentIndex \(currentIndex) postIndex \(postIndex) postfish \(postfish)") SCNTransaction.begin() SCNTransaction.animationDuration = 1 cylinderNode.simdEulerAngles.y -= GLKMathDegreesToRadians(Float(angle)) currentIndex += 1 currentIndex = currentIndex % 9 SCNTransaction.commit() if postfish == plainNodes.count - 1 { flip.toggle() } if flip { paperNodes[postfish].isHidden = false plainNodes[postfish].isHidden = true } if !flip { paperNodes[postfish].isHidden = true plainNodes[postfish].isHidden = false } case UISwipeGestureRecognizer.Direction.up: break case UISwipeGestureRecognizer.Direction.down: break default: assert(false,"NeverHappens") } } func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { self.trackingView.scene.rootNode.addChildNode(cylinderNode) cylinderNode.simdPosition.z -= 0.4 return nil let device = trackingView.device faceGeometry = ARSCNFaceGeometry(device: device!) faceNode = SCNNode(geometry: faceGeometry) faceNode.geometry?.firstMaterial?.fillMode = .lines faceNode.geometry?.firstMaterial?.diffuse.contents = UIColor.clear.withAlphaComponent(1) faceNode.addChildNode(cylinderNode) return faceNode } func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { guard let faceAnchor = anchor as? ARFaceAnchor else { return } faceGeometry.update(from: faceAnchor.geometry) } func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) { if textNodes[currentIndex].scale.x != 0.4 { for i in 0...7 { textNodes[i].scale = SCNVector3Make(0.04, 0.08, 0.04) let material = SCNMaterial() material.lightingModel = .physicallyBased material.metalness.contents = 0.2 material.roughness.contents = 0.5 //textNodes[i].geometry?.materials = [material] textNodes[i].geometry?.firstMaterial?.diffuse.contents = colours[i] } SCNTransaction.begin() SCNTransaction.animationDuration = 0.4 textNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black textNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04) textNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black textNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04) textNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white textNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06) shadowNodes[previousIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black shadowNodes[previousIndex].scale = SCNVector3Make(0.04, 0.10, 0.04) shadowNodes[postIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.black shadowNodes[postIndex].scale = SCNVector3Make(0.04, 0.10, 0.04) shadowNodes[currentIndex].geometry?.firstMaterial?.diffuse.contents = UIColor.white shadowNodes[currentIndex].scale = SCNVector3Make(0.04, 0.12, 0.06) SCNTransaction.commit() } if time > spawnTime { spawnTime = time + TimeInterval(1) } } func newTextNode(Text:String, newColor:UIColor) -> SCNNode { let textGeo = SCNText(string: Text, extrusionDepth: 0.01) let font = UIFont(name: "Neuton-Regular", size: 0.7) textGeo.font = font textGeo.firstMaterial!.diffuse.contents = newColor textGeo.firstMaterial?.fillMode = .fill textGeo.alignmentMode = CATextLayerAlignmentMode.center.rawValue textGeo.chamferRadius = 0.001 let (minBound, maxBound) = textGeo.boundingBox let textNode = SCNNode(geometry: textGeo) textNode.pivot = SCNMatrix4MakeTranslation( (maxBound.x - minBound.x)/2 , minBound.y / 0.80 , 0) textNode.scale = SCNVector3Make(0.04, 0.08, 0.04) return textNode } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }