Last active
March 24, 2023 14:39
-
-
Save wizard1066/764792d0632d4584e1a0a75a28b7b947 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | |
willSet { | |
//print("current \(currentIndex)") | |
// if currentIndex < 7 { | |
// postIndex = currentIndex + 2 | |
// } else { | |
// postIndex = 0 | |
// } | |
// prefish = previousIndex - 1 | |
// prefish = prefish % 9 | |
} | |
didSet { | |
previousIndex = currentIndex - 1 | |
if previousIndex < 0 { | |
previousIndex = 8 | |
} | |
previousIndex = previousIndex % 9 | |
prefish = previousIndex - 1 | |
if prefish < 0 { | |
prefish = 8 | |
} | |
prefish = prefish % 9 | |
// if currentIndex > coreNodes.count - 1 { | |
// currentIndex = 0 | |
// postIndex = coreNodes.count - 1 | |
// } | |
// if currentIndex < 0 { | |
// currentIndex = coreNodes.count - 1 | |
// postIndex = 0 | |
// } | |
// if currentIndex == 0 { | |
// postIndex = 1 | |
// } | |
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: "AvenirNext-Medium", 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() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment