Today a tutorial about augmented reality, in particular the use of the new Apple framework, ARKit, to measure distances between two points in a 3D virtual space.
How to?
First of all, as usual, you need to create a new XCode project. Select the “Augmented Reality App” type:
Open your ViewController.swift and remove the useless stuff, like:
let scene = SCNScene(named: "art.scnassets/ship.scn")!
sceneView.scene = scene
and all the ARSCNViewDelegate methods.
Well, your code is now clear!
You need to add now two variables, an array of SCNNode (the dots) and another SCNNode that is the text displayed on the screen.
Second thing is to add the touch handler, in this case, touchedBegan in order to add points:
var dotNodes = [SCNNode]()
var textNode = SCNNode()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { }
In the touchBegan, you need to recognize the number of touches, and add the green dots:
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
if dotNodes.count >= 2 { resetDots() }
if let touchLocation = touches.first?.location(in: sceneView) {
let hitTestResults = sceneView.hitTest(touchLocation, types: .featurePoint)
if let hitResult = hitTestResults.first {
addDot(at: hitResult)
}
}
}
Your addDot function, append on the main AR scene a child node, the dot, based on the user touch location.
func addDot( at hitResult: ARHitTestResult ) {
// the size of the green circle
let dotGeometry = SCNSphere(radius: 0.005)
let material = SCNMaterial()
material.diffuse.contents = UIColor.systemGreen
dotGeometry.materials = [material]
// the "dot" element
let dotNode = SCNNode(geometry: dotGeometry)
dotNode.position = SCNVector3(
hitResult.worldTransform.columns.3.x,
hitResult.worldTransform.columns.3.y,
hitResult.worldTransform.columns.3.z)
sceneView.scene.rootNode.addChildNode(dotNode)
dotNodes.append(dotNode)
// we have 2 points on scree, try to calculate distance
if dotNodes.count >= 2 {
calculateDistance()
}
}
and last one, calculateDistance calculate distance between points:
func calculateDistance() {
let start = dotNodes.first!
let end = dotNodes.last!
var distance = sqrt(
pow(end.position.x - start.position.x, 2) +
pow(end.position.y - start.position.y, 2) +
pow(end.position.z - start.position.z, 2)
)
// convert to cm
distance *= 100
let distanceFormatted = String(format: "%.2f cm", abs(distance))
updateText(text: distanceFormatted, atPosition: end.position)
}
Cool! We have a distance now.
The last cool thing is to write text on screen. We use updateText to draw on screen.
The text on screen works in the same way as the dot. Is a SCNode added on the root scene.
func updateText( text: String, atPosition: SCNVector3 ) {
textNode.removeFromParentNode()
let textGeometry = SCNText(string: text, extrusionDepth: 1.0)
textGeometry.firstMaterial?.diffuse.contents = UIColor.systemRed
textNode = SCNNode(geometry: textGeometry)
textNode.position = SCNVector3(
atPosition.x,
atPosition.y + 0.01,
atPosition.z
)
textNode.scale = SCNVector3(0.01, 0.01, 0.01)
sceneView.scene.rootNode.addChildNode(textNode)
}
That’s all. It is very easy.
Clear all points:
func resetDots() {
for dot in dotNodes {
dot.removeFromParentNode()
}
dotNodes = [SCNNode]()
}
As final result you should see something like this:
Enjoy measuring things!