ARKit Place a SCNNode facing the camera

Multi tool use
ARKit Place a SCNNode facing the camera
I'm using ARKit
to display 3D objects. I managed to place the nodes in the real world in front of the user (aka the camera). But I don't manage to make them to face the camera when I drop them.
ARKit
let tap_point=CGPoint(x: x, y: y)
let results=arscn_view.hitTest(tap_point, types: .estimatedHorizontalPlane)
guard results.count>0 else{
return
}
guard let r=results.first else{
return
}
let hit_tf=SCNMatrix4(r.worldTransform)
let new_pos=SCNVector3Make(hit_tf.m41, hit_tf.m42+Float(0.2), hit_tf.m43)
guard let scene=SCNScene(named: file_name) else{
return
}
guard let node=scene.rootNode.childNode(withName: "Mesh", recursively: true) else{
return
}
node.position=new_pos
arscn_view.scene.rootNode.addChildNode(node)
The nodes are well positioned on the plane, in front of the camera. But they are all looking in the same direction. I guess I should rotate the SCNNode
but I didn't manage to do this.
SCNNode
4 Answers
4
First, get the rotation matrix of the camera:
let rotate = simd_float4x4(SCNMatrix4MakeRotation(sceneView.session.currentFrame!.camera.eulerAngles.y, 0, 1, 0))
Then, combine the matrices:
let rotateTransform = simd_mul(r.worldTransform, rotate)
Lastly, apply a transform to your node, casting as SCNMatrix4:
node.transform = SCNMatrix4(rotateTransform)
Hope that helps
EDIT
here how you can create SCNMatrix4 from simd_float4x4
let rotateTransform = simd_mul(r.worldTransform, rotate)
node.transform = SCNMatrix4(m11: rotateTransform.columns.0.x, m12: rotateTransform.columns.0.y, m13: rotateTransform.columns.0.z, m14: rotateTransform.columns.0.w, m21: rotateTransform.columns.1.x, m22: rotateTransform.columns.1.y, m23: rotateTransform.columns.1.z, m24: rotateTransform.columns.1.w, m31: rotateTransform.columns.2.x, m32: rotateTransform.columns.2.y, m33: rotateTransform.columns.2.z, m34: rotateTransform.columns.2.w, m41: rotateTransform.columns.3.x, m42: rotateTransform.columns.3.y, m43: rotateTransform.columns.3.z, m44: rotateTransform.columns.3.w)
"r" is just the TC's variable for results.first, meaning the user's first touch point on the screen (I'm assuming within their touchesBegan method). The documentation explains it best, worldTransform is "The transformation matrix that defines the intersection’s rotation, translation and scale relative to the world."
– modium
Jan 23 at 20:53
Ahh okay, i get what world transform is/does, just not where r came from, thanks!
– Jake Dobson
Jan 23 at 20:55
thank you!!! I swear yours is the only answer on stack overflow that worked for me! adding a node as child of camera would make the node follow it, with your way instead it is only initially oriented towards the camera but stays in place. in my case I would extract a
SCNVector3
inside touchesBegan
to use as position of my textNode
and then apply the rotateTransform
as you showed. Thanks Again, this should be marked as the answer @op– MaX
Apr 5 at 7:50
SCNVector3
touchesBegan
textNode
rotateTransform
No problem! Glad it helped you out.
– modium
Apr 7 at 5:42
Do you want the nodes to always face the camera, even as the camera moves? That's what SceneKit constraints are for. Either SCNLookAtConstraint
or SCNBillboardConstraint
can keep a node always pointing at the camera.
SCNLookAtConstraint
SCNBillboardConstraint
Do you want the node to face the camera when placed, but then hold still (so you can move the camera around and see the back of it)? There are a few ways to do that. Some involve fun math, but a simpler way to handle it might just be to design your 3D assets so that "front" is always in the positive Z-axis direction. Set a placed object's transform based on the camera transform, and its initial orientation will match the camera's.
The second :) I want the node to face the camera when I place it then keep it here (and be able to move around).
– Marie Dm
Sep 13 '17 at 15:55
I don't understand what you mean when you say > design your 3D assets so that "front" is always in the positive Z-axis direction. I have a dae file I converted to scn in xCode. The node position is (0, 0, 0). But I also placed the camera at (0, 0, 0.9) in order to have the object in front of me. Might be important to precise the camera is in the node hierarchy (a subnode I guess).
– Marie Dm
Sep 13 '17 at 15:59
Nodes are always right positioned in front of the camera when I drop them, they are visible (no behind the camera). If I move in the room and drop several nodes they are all kind of parallels. When I drop the first node, it is "looking at me". But not the next ones. I don't know if what I say is clear... :/
– Marie Dm
Sep 13 '17 at 16:06
If you are bringing your DAE in from Blender you have to rotate it -90 degrees.
– PruitIgoe
Oct 22 '17 at 14:05
@MarieDm did you resolve this issue? I am also facing the same issue and couldn't find a concrete solution for this.
– Venkatesh
Dec 1 '17 at 7:01
Here's how I did it:
func faceCamera() {
guard constraints?.isEmpty ?? true else {
return
}
SCNTransaction.begin()
SCNTransaction.animationDuration = 5
SCNTransaction.completionBlock = { [weak self] in
self?.constraints =
}
constraints = [billboardConstraint]
SCNTransaction.commit()
}
private lazy var billboardConstraint: SCNBillboardConstraint = {
let constraint = SCNBillboardConstraint()
constraint.freeAxes = [.Y]
return constraint
}()
As stated earlier a SCNBillboardConstraint
will make the node always look at the camera. I am animating it so the node doesn't just immediately snap into place, this is optional. In the SCNTransaction.completionBlock
I remove the constraint, also optional.
SCNBillboardConstraint
SCNTransaction.completionBlock
Also I set the SCNBillboardConstraint
's freeAxes
, which customizes on what axis the node follows the camera, again optional.
SCNBillboardConstraint
freeAxes
here's my code for the SCNNode facing the camera..hope help for someone
let location = touches.first!.location(in: sceneView)
var hitTestOptions = [SCNHitTestOption: Any]()
hitTestOptions[SCNHitTestOption.boundingBoxOnly] = true
let hitResultsFeaturePoints: [ARHitTestResult] = sceneView.hitTest(location, types: .featurePoint)
let hitTestResults = sceneView.hitTest(location)
guard let node = hitTestResults.first?.node else {
if let hit = hitResultsFeaturePoints.first {
let rotate = simd_float4x4(SCNMatrix4MakeRotation(sceneView.session.currentFrame!.camera.eulerAngles.y, 0, 1, 0))
let finalTransform = simd_mul(hit.worldTransform, rotate)
sceneView.session.add(anchor: ARAnchor(transform: finalTransform))
}
return
}
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
What is "r.worldTransform" referring to? I don't see what it could be.
– Jake Dobson
Jan 23 at 17:46