Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class Plane: SCNNode {
- var anchor :ARPlaneAnchor
- var planeGeometry :SCNPlane!
- init(anchor :ARPlaneAnchor) {
- self.anchor = anchor
- super.init()
- setup()
- }
- func update(anchor: ARPlaneAnchor) {
- self.planeGeometry.width = CGFloat(anchor.extent.x)
- self.planeGeometry.height = CGFloat(anchor.extent.z)
- self.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
- let planeNode = self.childNodes.first!
- planeNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: self.planeGeometry, options: nil))
- }
- private func setup() {
- //plane dimensions
- self.planeGeometry = SCNPlane(width: CGFloat(self.anchor.extent.x), height: CGFloat(self.anchor.extent.z))
- //plane material
- let material = SCNMaterial()
- material.diffuse.contents = UIImage(named: "tronGrid.png")
- self.planeGeometry.materials = [material]
- //plane geometry and physics
- let planeNode = SCNNode(geometry: self.planeGeometry)
- planeNode.physicsBody = SCNPhysicsBody(type: .static, shape: SCNPhysicsShape(geometry: self.planeGeometry, options: nil))
- planeNode.physicsBody?.categoryBitMask = BodyType.plane.rawValue
- planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
- planeNode.transform = SCNMatrix4MakeRotation(Float(-Double.pi / 2.0), 1, 0, 0)
- //add plane node
- self.addChildNode(planeNode)
- }
- enum BodyType: Int {
- case box = 1
- case pyramid = 2
- case plane = 3
- }
- class ViewController: UIViewController, ARSCNViewDelegate, SCNPhysicsContactDelegate {
- //outlets
- @IBOutlet var sceneView: ARSCNView!
- //globals
- var planes = [Plane]()
- var boxes = [SCNNode]()
- //life cycle
- override func viewDidLoad() {
- super.viewDidLoad()
- //set sceneView's frame
- self.sceneView = ARSCNView(frame: self.view.frame)
- //add debugging option for sceneView (show x, y , z coords)
- self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints, ARSCNDebugOptions.showWorldOrigin]
- //give lighting to the scene
- self.sceneView.autoenablesDefaultLighting = true
- //add subview to scene
- self.view.addSubview(self.sceneView)
- // Set the view's delegate
- sceneView.delegate = self
- //subscribe to physics contact delegate
- self.sceneView.scene.physicsWorld.contactDelegate = self
- //show statistics such as fps and timing information
- sceneView.showsStatistics = true
- //create new scene
- let scene = SCNScene()
- //set scene to view
- sceneView.scene = scene
- //setup recognizer to add scooter to scene
- let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped))
- sceneView.addGestureRecognizer(tapGestureRecognizer)
- }
- //MARK: helper funcs
- //private var boxStatus: BoxStatus = .notAdded
- @objc func tapped(recognizer: UIGestureRecognizer) {
- let scnView = recognizer.view as! ARSCNView
- let touchLocation = recognizer.location(in: scnView)
- let touch = scnView.hitTest(touchLocation, types: .existingPlaneUsingExtent)
- //take action if user touches box
- if !touch.isEmpty {
- guard let hitResult = touch.first else { return }
- addBox(hitResult: hitResult)
- }
- }
- // private func nodeForScene(sceneName: String, nodeName: String) -> SCNNode? {
- // let scn = SCNScene(named: sceneName)!
- // return scn.rootNode.childNode(withName: nodeName, recursively: true)
- // }
- private func addBox(hitResult: ARHitTestResult) {
- let boxGeometry = SCNBox(width: 0.1,
- height: 0.1,
- length: 0.1,
- chamferRadius: 0)
- let material = SCNMaterial()
- material.diffuse.contents = UIColor(red: .random(),
- green: .random(),
- blue: .random(),
- alpha: 1.0)
- boxGeometry.materials = [material]
- let boxNode = SCNNode(geometry: boxGeometry)
- //adding physics body, a box already has a shape, so nil is fine
- boxNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
- //set bitMask on boxNode, enabling objects with diff categoryBitMasks to collide w/ each other
- boxNode.physicsBody?.categoryBitMask = BodyType.plane.rawValue | BodyType.box.rawValue
- boxNode.position = SCNVector3(hitResult.worldTransform.columns.3.x,
- hitResult.worldTransform.columns.3.y + 0.3,
- hitResult.worldTransform.columns.3.z)
- self.sceneView.scene.rootNode.addChildNode(boxNode)
- }
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
- let configuration = ARWorldTrackingConfiguration()
- configuration.planeDetection = .horizontal
- //track objects in ARWorld and start session
- sceneView.session.run(configuration)
- }
- //MARK: - ARSCNViewDelegate
- func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
- //if no anchor found, don't render anything!
- if !(anchor is ARPlaneAnchor) {
- return
- }
- DispatchQueue.main.async {
- //add plane to scene
- let plane = Plane(anchor: anchor as! ARPlaneAnchor)
- self.planes.append(plane)
- node.addChildNode(plane)
- //add initial scene object
- let pyramidGeometry = SCNPyramid(width: CGFloat(plane.planeGeometry.width / 8), height: plane.planeGeometry.height / 8, length: plane.planeGeometry.height / 8)
- pyramidGeometry.firstMaterial?.diffuse.contents = UIColor.white
- let pyramidNode = SCNNode(geometry: pyramidGeometry)
- pyramidNode.name = "pyramid"
- pyramidNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
- pyramidNode.physicsBody?.categoryBitMask = BodyType.pyramid.rawValue | BodyType.plane.rawValue
- pyramidNode.physicsBody?.contactTestBitMask = BodyType.box.rawValue
- pyramidNode.position = SCNVector3(-(plane.planeGeometry.width) / 3, 0, plane.planeGeometry.height / 3)
- node.addChildNode(pyramidNode)
- }
- }
- func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
- let plane = self.planes.filter {
- plane in return plane.anchor.identifier == anchor.identifier
- }.first
- if plane == nil {
- return
- }
- plane?.update(anchor: anchor as! ARPlaneAnchor)
- }
- override func viewWillDisappear(_ animated: Bool) {
- super.viewWillDisappear(animated)
- //pause session
- sceneView.session.pause()
- }
- }
Add Comment
Please, Sign In to add comment