Search code examples
swiftaugmented-realityarkitscnnodescnscene

SCNNode facing towards the camera


I am trying to put SCNCylinder node in the scene on the touch point. I always want to show the cylinder shape diameter facing towards camera. Its working fine for horizontal scene but it have a problem in vertical scene. In vertical scene I can see the cylinder sides but I want to show the full diameter facing towards the camera no matter whats the camera orientation is. I know there is some transformation needs to be applied depending on the camera transform but don't know how. I am not using plane detection its the simple node which is directly added to the scene.

Vertical Image: enter image description here

Horizontal Image: enter image description here

The code to insert the node is as follows,

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
               guard let touch = touches.first else {
                    return
                }
                let result = sceneView.hitTest(touch.location(in: sceneView), types: [ARHitTestResult.ResultType.featurePoint])
                guard let hitResult = result.last else {
                    print("returning because couldn't find the touch point")
                    return
                }
                let hitTransform = SCNMatrix4(hitResult.worldTransform)
                let position = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m43)
                let ballShape = SCNCylinder(radius: 0.02, height: 0.01)

                let ballNode = SCNNode(geometry: ballShape)
                ballNode.position = position
                sceneView.scene.rootNode.addChildNode(ballNode)
}

Any help would be appreciated.


Solution

  • I'm not certain this is the right way to handle what you need but here is something which may help you.

    I think CoreMotion could be useful to help you determine if the device is at a horizontal or vertical angle.

    enter image description here

    This class has a property called attitude, which describes the rotation of our device in terms of roll, pitch, and yaw. If we are holding our phone in portrait orientation, the roll describes the angle of rotation about the axis that runs through the top and bottom of the phone. The pitch describes the angle of rotation about the axis that runs through the sides of your phone (where the volume buttons are). And finally, the yaw describes the angle of rotation about the axis that runs through the front and back of your phone. With these three values, we can determine how the user is holding their phone in reference to what would be level ground (Stephan Baker).

    Begin by importing CoreMotion:

    import CoreMotion
    

    Then create the following variables:

     let deviceMotionDetector = CMMotionManager()
     var currentAngle: Double!
    

    We will then create a function which will check the angle of our device like so:

       /// Detects The Angle Of The Device
    func detectDeviceAngle(){
    
        if deviceMotionDetector.isDeviceMotionAvailable == true {
    
            deviceMotionDetector.deviceMotionUpdateInterval = 0.1;
    
            let queue = OperationQueue()
    
            deviceMotionDetector.startDeviceMotionUpdates(to: queue, withHandler: { (motion, error) -> Void in
    
                if let attitude = motion?.attitude {
    
                    DispatchQueue.main.async {
    
                        let pitch = attitude.pitch * 180.0/Double.pi
                        self.currentAngle = pitch
                        print(pitch)
    
                    }
                }
    
            })
    
        }
        else {
            print("Device Motion Unavailable");
        }
    
    }
    

    This only needs to be called once for example in viewDidLoad:

     detectDeviceAngle()
    

    In your touchesBegan method you can add this to the end:

    //1. If We Are Holding The Device Above 60 Degress Change The Node
    if currentAngle > 60 {
    
        //2a. Get The X, Y, Z Values Of The Desired Rotation
        let rotation = SCNVector3(1, 0, 0)
        let vector3x = rotation.x
        let vector3y = rotation.y
        let vector3z = rotation.z
        let degreesToRotate:Float = 90
    
        //2b. Set The Position & Rotation Of The Object
        sphereNode.rotation = SCNVector4Make(vector3x, vector3y, vector3z, degreesToRotate * 180 / .pi)
    
    }else{
    
    }
    

    I am sure there are better ways to achieve what you need (and I would be very interested in hearing them too), but I hope it will get you started.

    Here is the result:

    enter image description here