How to get the SCNVector3 position of the camera in relation to it's direction ARKit Swift
Edited to better answer the question now that it's clarified in a comment
New Answer:
You are using only the position of the camera, so if the camera is rotated, it doesn't affect the ball.
What you can do is get the transform matrix of the ball and multiply it by the transform matrix of the camera, that way the ball position will be relative to the full transformation of the camera, including rotation.
e.g.
let ballShape = SCNSphere(radius: 0.03)
let ballNode = SCNNode(geometry: ballShape)
ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
let ballMatrix = ballNode.transform
let cameraMatrix = sceneView.pointOfView!.transform
let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
ballNode.transform = newBallMatrix
sceneView.scene.rootNode.addChildNode(ballNode)
Or if you only want the SCNVector3 position, to answer exactly to your question (this way the ball will not rotate):
...
let newBallMatrix = SCNMatrix4Mult(ballMatrix, cameraMatrix)
let newBallPosition = SCNVector3Make(newBallMatrix.m41, newBallMatrix.m42, newBallMatrix.m43)
ballNode.position = newBallPosition
sceneView.scene.rootNode.addChildNode(ballNode)
Old Answer:
You are using only the position of the camera, so when the camera rotates, it doesn't affect the ball.
SceneKit uses a hierarchy of nodes, so when a node is "child" of another node, it follows the position, rotation and scale of its "parent". The proper way of attaching an object to another object, in this case the camera, is to make it "child" of the camera.
Then, when you set the position, rotation or any other aspect of the transform of the "child" node, you are setting it relative to its parent. So if you set the position to SCNVector3Make(0.0, 0.0, -0.4)
, it's translated -0.4 units in Z on top of its "parent" translation.
So to make what you want, it should be:
let ballShape = SCNSphere(radius: 0.03)
let ballNode = SCNNode(geometry: ballShape)
ballNode.position = SCNVector3Make(0.0, 0.0, -0.4)
let cameraNode = sceneView.pointOfView
cameraNode?.addChildNode(ballNode)
This way, when the camera rotates, the ball follows exactly its rotation, but separated -0.4 units from the camera.
SCNVector3 to SCNVector3 in 2D arrow direction
The projectPoint(_:)
API is what you need:
Projects a point from the 3D world coordinate system of the scene to the 2D pixel coordinate system of the renderer.
SceneKit – Get direction of camera
You can create an SCNNode that place it in worldFront property to get a vector with the x, y, and z direction.
Another way you could do it is like how this project did it:
// Credit to https://github.com/farice/ARShooter
func getUserVector() -> (SCNVector3, SCNVector3) { // (direction, position)
if let frame = self.sceneView.session.currentFrame {
let mat = SCNMatrix4(frame.camera.transform) // 4x4 transform matrix describing camera in world space
let dir = SCNVector3(-1 * mat.m31, -1 * mat.m32, -1 * mat.m33) // orientation of camera in world space
let pos = SCNVector3(mat.m41, mat.m42, mat.m43) // location of camera in world space
return (dir, pos)
}
return (SCNVector3(0, 0, -1), SCNVector3(0, 0, -0.2))
}
Position a SceneKit object in front of SCNCamera's current orientation
(Swift 4)
Hey, you can use this simpler function if you want to put the object a certain position relative to another node (e.g. the camera node) and also in the same orientation as the reference node:
func updatePositionAndOrientationOf(_ node: SCNNode, withPosition position: SCNVector3, relativeTo referenceNode: SCNNode) {
let referenceNodeTransform = matrix_float4x4(referenceNode.transform)
// Setup a translation matrix with the desired position
var translationMatrix = matrix_identity_float4x4
translationMatrix.columns.3.x = position.x
translationMatrix.columns.3.y = position.y
translationMatrix.columns.3.z = position.z
// Combine the configured translation matrix with the referenceNode's transform to get the desired position AND orientation
let updatedTransform = matrix_multiply(referenceNodeTransform, translationMatrix)
node.transform = SCNMatrix4(updatedTransform)
}
If you'd like to put 'node' 2m right in front of a certain 'cameraNode', you'd call it like:
let position = SCNVector3(x: 0, y: 0, z: -2)
updatePositionAndOrientationOf(node, withPosition: position, relativeTo: cameraNode)
Edit: Getting the camera node
To get the camera node, it depends if you're using SceneKit, ARKit, or other framework. Below are examples for ARKit and SceneKit.
With ARKit, you have ARSCNView to render the 3D objects of an SCNScene overlapping the camera content. You can get the camera node from ARSCNView's pointOfView property:
let cameraNode = sceneView.pointOfView
For SceneKit, you have an SCNView that renders the 3D objects of an SCNScene. You can create camera nodes and position them wherever you want, so you'd do something like:
let scnScene = SCNScene()
// (Configure scnScene here if necessary)
scnView.scene = scnScene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(0, 5, 10) // For example
scnScene.rootNode.addChildNode(cameraNode)
Once a camera node has been setup, you can access the current camera in the same way as ARKit:
let cameraNode = scnView.pointOfView
ARKit – Get current position of ARCamera in a scene
Set yourself as the ARSession.delegate
. Than you can implement session(_:didUpdate:)
which will give you an ARFrame
for every frame processed in your session. The frame has an camera
property that holds information on the cameras transform, rotation and position.
func session(_ session: ARSession, didUpdate frame: ARFrame) {
// Do something with the new transform
let currentTransform = frame.camera.transform
doSomething(with: currentTransform)
}
As rickster pointed out you always can get the current ARFrame
and the camera position through it by calling session.currentFrame
.
This is useful if you need the position just once, eg to move a node where the camera has been but you should use the delegate
method if you want to get updates on the camera's position.
Scenekit move object with respect to camera
Here is the solution
guard let front = sceneView.pointOfView?.simdWorldFront else {return}
let horazontalRight = cross(front, simd_float3(0,1,0))
// for moving right
node.position += SCNVector3(horazontalRight * 0.01)
Related Topics
Saving Coredata to a Web Server with Swift 3.0
Solving System of Equations in Swift
Zoom to Fit Current Location and Annotation on Map
Error "No Such Module" When Installed Framework with Pod in Swift 3
Swift:Pause and Resume Nstimer
JSONserialization.JSONobject Performance in Swift
How to Cancel Firebase Setvalue While Pending for Completion (When Offline)
Color Keying Video with Gpuimage on a Scnplane in Arkit
Loading Multiple Google Interstitial Ads Makes App Crash
Appdelegate Segue Alternative Pass Data
Best Way to Structure My Firebase Database
Get Current Url from Browser in MACos
Create a Loading Image/ Activity Indicator, Until the Image Is Shown in the Screen in Swift
Non-Modular Headers of Openssl Library When Using Modulemap for Swift Framework