8

The code below places a SCNPlane onto the point touched, but the plane is oriented (rotated) to the position that the phone was in when the app started. I would ideally like to orient the Node to a physical wall or to the current orientation of the camera. How is this done with ARKit?

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 {return}
    let hitTransform = SCNMatrix4.init(hitResult.worldTransform)

    let hitVector = SCNVector3Make(hitTransform.m41, hitTransform.m42, hitTransform.m43)
    createPlane(position: hitVector)
}

func createPlane(position: SCNVector3) {

    let background = SCNNode()
    background.geometry = SCNPlane.init(width: 0.12, height: 0.10) // better set its size
    background.geometry?.firstMaterial?.diffuse.contents = "odinBW2.jpeg"
    background.position = position

    sceneView.scene.rootNode.addChildNode(background)

}
rickster
  • 124,678
  • 26
  • 272
  • 326
Rob Schlackman
  • 309
  • 3
  • 6

2 Answers2

11

If you want to align your node to the camera's orientation, just use SCNBillboardConstraint.

let yourNode = SCNNode()

let billboardConstraint = SCNBillboardConstraint()
billboardConstraint.freeAxes = [.X, .Y, .Z]
yourNode.constraints = [billboardConstraint]
Jun Tanaka
  • 111
  • 1
  • 3
  • This is pretty awesome. Thanks! – Cesare Dec 16 '18 at 21:27
  • `billboardConstraint.freeAxes = [.X, .Y, .Z]` is redundant, that's the default (`SCNBillboardAxis.all`). Can just do `yourNode.constraints = [SCNBillboardConstraint()]` – bauerMusic Sep 13 '21 at 06:56
7

1 - This is how you get the rotation of the camera first:

Pitch - rotation around the X axis

let pitch = sceneView.session.currentFrame?.camera.eulerAngles.x 

Yawn - rotation around the Y axis

let yawn = sceneView.session.currentFrame?.camera.eulerAngles.y

Roll - rotation around the Z axis

let roll = sceneView.session.currentFrame?.camera.eulerAngles.z

2 - And then you can create the SCNVector3 that matches the rotations you want to keep (0 if you don't want any rotation on an axis) :

let newRotation = SCNVector3Make(pitch, yawn, roll)

3 - And then you attribute this eulerAngles to your node:

whateverNode.eulerAngles = newRotation
Oscar Falmer
  • 1,771
  • 1
  • 24
  • 38