Setting Arkit Orientation via Swift

Setting ARKit Orientation via Swift

I was able to solve this in the application logic by multiplying the camera matrix by a quaternion that is rotated based on device orientation.

How can I set text orientation in ARKit?

Am I correct in saying that you want the text to face the camera when you tap (wherever you happen to be facing), but then remain stationary?

There are a number of ways of adjusting the orientation of any node. For this case I would suggest simply setting the eulerAngles of the text node to be equal to those of the camera, at the point in which you instantiate the text.

In your addTag() function you add:

let eulerAngles = self.sceneView.session.currentFrame?.camera.eulerAngles
tagNode.eulerAngles = SCNVector3(eulerAngles.x, eulerAngles.y, eulerAngles.z + .pi / 2)

The additional .pi / 2 is there to ensure the text is in the correct orientation, as the default with ARKit is for a landscape orientation and therefore the text comes out funny. This applies a rotation around the local z axis.

It's also plausible (and some may argue it's better) to use .localRotate() of the node, or to access its transform property, however I like the approach of manipulating both the position and eulerAngles directly.

Hope this helps.

EDIT: replaced Float(1.57) with .pi / 2.

ARKit camera initial position?

To change the default orientation of your model, first select your model file from Xcode navigator then set camera Point of view to Front (from the bottom bar). This will show the exact view of your model when it is loaded in your scene.

Then, to change the position and orientation of the model, select the Node inspector from the right side menu (the cube icon) and change the Euler x, y, z values.

Orienting a directional light and adding to scene in ARKit

Use the following code to control orientation of directional light:

Take into consideration that position of Directional Light is not important!

import ARKit
import RealityKit

class Lighting: Entity, HasDirectionalLight, HasAnchoring {

required init() {
super.init()

self.light = DirectionalLightComponent(color: .green,
intensity: 1000,
isRealWorldProxy: true)
}
}

class ViewController: UIViewController {

@IBOutlet var arView: ARView!

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)

let light = Lighting()
light.orientation = simd_quatf(angle: .pi/8,
axis: [0, 1, 0])

let boxAnchor = try! Experience.loadBox()

let directLightAnchor = AnchorEntity()
directLightAnchor.addChild(light)

boxAnchor.addChild(directLightAnchor)
boxAnchor.steelBox!.scale = [30,30,30]
boxAnchor.steelBox!.position.z = -3

arView.scene.anchors.append(boxAnchor)
}
}

If you want to know how implement directional light's orientation in SceneKit, read this post.

Sample Image

Setting up the Orientation of 3D Text in Arkit application

Looking at your question I am assuming that you want to display your SCNText centrally between your two points and above the line which is placed between them, and respond to the changing position of the camera, e.g. always face the current pointOfView.

As such I have created an SCNNode Subclass which handles this with relative ease:

class TextNode: SCNNode{

var textGeometry: SCNText!

/// Creates An SCNText Geometry
///
/// - Parameters:
/// - text: String (The Text To Be Displayed)
/// - depth: Optional CGFloat (Defaults To 1)
/// - font: UIFont
/// - textSize: Optional CGFloat (Defaults To 3)
/// - colour: UIColor
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 0.1, colour: UIColor) {

super.init()

//1. Create A Billboard Constraint So Our Text Always Faces The Camera
let constraints = SCNBillboardConstraint()

//2. Create An SCNNode To Hold Out Text
let node = SCNNode()
let max, min: SCNVector3
let tx, ty, tz: Float

//3. Set Our Free Axes
constraints.freeAxes = .Y

//4. Create Our Text Geometry
textGeometry = SCNText(string: text, extrusionDepth: depth)

//5. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0

//6. Set The Alignment Mode Of The Text
textGeometry.alignmentMode = kCAAlignmentCenter

//7. Set Our Text Colour & Apply The Font
textGeometry.firstMaterial?.diffuse.contents = colour
textGeometry.firstMaterial?.isDoubleSided = true
textGeometry.font = UIFont(name: font, size: textSize)

//8. Position & Scale Our Node
max = textGeometry.boundingBox.max
min = textGeometry.boundingBox.min

tx = (max.x - min.x) / 2.0
ty = min.y
tz = Float(depth) / 2.0

node.geometry = textGeometry
node.scale = SCNVector3(0.001, 0.001, 0.001)
node.pivot = SCNMatrix4MakeTranslation(tx, ty, tz)

self.addChildNode(node)

self.constraints = [constraints]

}

/// Places The TextNode Between Two SCNNodes
///
/// - Parameters:
/// - nodeA: SCNode
/// - nodeB: SCNode
func placeBetweenNodes(_ nodeA: SCNNode, and nodeB: SCNNode){

let minPosition = nodeA.position
let maxPosition = nodeB.position
let x = ((maxPosition.x + minPosition.x)/2.0)
let y = (maxPosition.y + minPosition.y)/2.0 + 0.01
let z = (maxPosition.z + minPosition.z)/2.0
self.position = SCNVector3(x, y, z)
}

required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }

}

An example of how to use this is as follows (note I haven't put a line between the nodes) but nevertheless this should point you in the right direction:

/// Generates Two SCNNodes & Places An SCNTextGeometry Between Them
func generateNodes(){

//1. Create An SCNSphere
let sphereNode = SCNNode()
let sphereNodeGeometry = SCNSphere(radius: 0.01)
sphereNodeGeometry.firstMaterial?.diffuse.contents = UIColor.cyan
sphereNode.geometry = sphereNodeGeometry

//2. Add The Starter Node And Place It 0.5m To The Left Of Our World Origin
let starterNode = sphereNode.clone()
starterNode.position = SCNVector3(-0.5, 0, -0.5)
self.augmentedRealityView.scene.rootNode.addChildNode(starterNode)

//3. Add The End Node And Place It 0.5m To The Right Of Our World Origin
let endNode = sphereNode.clone()
endNode.position = SCNVector3(0.5, 0, -0.5)
self.augmentedRealityView.scene.rootNode.addChildNode(endNode)

//4. Although We Know The Distance Between The Nodes Is 1m, Calculate It Anyway
if let formattedDistance = calculateDistanceBetween(starterNode, and: endNode)?.distance{

//5. Create The Distance Label
let distanceLabel = TextNode(text: "\(formattedDistance)m", colour: .white)

//6. Call The TextNode Place Between Nodes Function To Center The Text Between The Nodes
distanceLabel.placeBetweenNodes(starterNode, and: endNode)

//7. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(distanceLabel)
}

}

/// Calculates The Distance Between Two SCNNodes
///
/// -
/// - Returns: (distance: Float, nodeA: GLKVector3, nodeB: GLKVector3)
func calculateDistanceBetween(_ starterNode: SCNNode, and endNode: SCNNode) -> (distance: Float, nodeA: GLKVector3, nodeB: GLKVector3)?{

//1. Convert The SCNVector3 Positions To GLKVector3
let nodeAVector3 = GLKVector3Make(starterNode.position.x, starterNode.position.y, starterNode.position.z)
let nodeBVector3 = GLKVector3Make(endNode.position.x, endNode.position.y, endNode.position.z)

//2. Calculate The Distance
let distance = GLKVector3Distance(nodeAVector3, nodeBVector3)
let meters = Measurement(value: Double(distance), unit: UnitLength.meters)

print("Distance Between Markers Nodes = \(String(format: "%.2f", meters.value))m")

//4. Return The Distance A Positions Of The Nodes
return (distance: distance , nodeA: nodeAVector3, nodeB: nodeBVector3)

}

If your'e interested, you can get a more in depth and fully commented example from here: Measuring In ARKit

Hope it helps...



Related Topics



Leave a reply



Submit