Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine if an ScnNode is left or right of the view direction of a camera node in SceneKit

I have a camera in the center of SceneKit scene at position(0,0,0) I'm rotation the camera around the y-axis to show different parts of the scene.

I want to show arrows at the left/right side of my screen to show in which direction the user has to move to view a target object.

How can I calculate if a ScnNode is positioned at the left of right side of my view direction?

enter image description here

I know:

  • position and rotation of the camera node
  • position of the target node
like image 521
Stefan Avatar asked Jan 10 '17 22:01

Stefan


2 Answers

  1. Calculate the vector to the node relative to the camera. Calculate the vector by subtracting the camera position from the node position
  2. Use atan2 to convert the vector into an angle. The Apple docs are bit thin on explanation. There's a tutorial on raywenderlich.com.

E.g.

let v = CGPoint(x: nodePosition.x - cameraPosition.x, y: nodePosition.y - cameraPosition.y);
let a = atan2(v.y, v.x) // Note: order of arguments

The angle is in radians, and will range between -π and π (-180 to 180 degrees). If the value is negative the node is to the left of the camera vector, positive values mean the node is to the right, and zero means the node is straight ahead.

Conveniently, you can use this angle as-is to actually perform the rotation if you want. Just increment the camera angle by some fraction of the angle to the node.

E.g.

cameraRotation += a * 0.1

Caveats:

  • "Forward" or "Up" is not necessarily going to be the same as your internal coordinate system. Usually 0 is to the right, so if you want to show an arrow or other UI you may need to add or subtract an amount from the angle (usually -(π/2) to rotate a quarter turn).
  • The angle will always only be between -π and +π. If the node happens to fall directly behind the camera (exactly 180 degrees) you will get either -π or +π, and you might see the angle jump between these two extremes as the node cross over this discontinuity. You sometimes see the side effects of this in games where a nav arrow will point to a target then suddenly flick to the opposite side of the screen. There are various ways to handle this. One easy way is to use a low pass filter on the angle (ie accumulate multiple samples over time).
like image 160
Luke Van In Avatar answered Sep 28 '22 07:09

Luke Van In


There is an easier way with projectPoint funcion witch show the image as 2d element in your view and based on where it is you can locate where the user should look

func getIfHeIsLookingNew(sceneWidth: CGFloat, nodePosition: SCNVector3){
    if(nodePosition.z < 1){
        if(nodePosition.x > (Float(sceneWidth))){
            print("Look Right")
        }else if(nodePosition.x < 0){
            print("Look Left")
        }else{
            print("Correct")
            return
        }
    }else if(nodePosition.x < 0){
        print("Look Right")
    }else{
        print("Look Left")
    }
}

And use it like below

let sceneWidth:CGFloat = self.sceneView.frame.width

getIfHeIsLookingNew(sceneWidth: sceneWidth, nodePosition: self.sceneView.projectPoint(targetNode.position))
like image 40
Vasilis D. Avatar answered Sep 28 '22 08:09

Vasilis D.