Closed erikuecke closed 2 years ago
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor
else {
return
}
let planeGeometry = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
planeGeometry.firstMaterial?.diffuse.contents = UIColor.blue
let planeNode = SCNNode(geometry: planeGeometry)
planeNode.simdPosition = planeAnchor.center
planeNode.opacity = 0.5
planeNode.eulerAngles.x = -.pi / 2
node.addChildNode(planeNode)
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// 1
guard let planeAnchor = anchor as? ARPlaneAnchor, let planeNode = node.childNodes.first, let plane = planeNode.geometry as? SCNPlane else {
return
}
// 2
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
plane.width = width
plane.height = height
let x = CGFloat(planeAnchor.center.x)
let y = CGFloat(planeAnchor.center.y)
let z = CGFloat(planeAnchor.center.z)
planeNode.position = SCNVector3(x, y, z)
}
Something like above would give the user the "Blue Ocean" to place their ships on.
@erikuecke There is missing instruction in the markdown file, as you've pointed out. I'll check in with the publisher for an update (maybe after WWDC22). For readers following the book, please check out the instruction below in the chapter:
. . .
Your setUpSceneView()
method should now look like this:
func setUpSceneView() {
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
sceneView.session.run(configuration)
sceneView.delegate = self
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
}
--- Missing Instruction [Begin] ---
Now that the app gets notified every time a new ARAnchor is being added onto sceneView
, we may be interested in seeing what that newly added ARAnchor looks like.
Hence, update the renderer(_:didAdd:for:)
method like this:
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// 1
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
// 2
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
let plane = SCNPlane(width: width, height: height)
// 3
plane.materials.first?.diffuse.contents = UIColor.transparentLightBlue
// 4
let planeNode = SCNNode(geometry: plane)
// 5
let x = CGFloat(planeAnchor.center.x)
let y = CGFloat(planeAnchor.center.y)
let z = CGFloat(planeAnchor.center.z)
planeNode.position = SCNVector3(x,y,z)
planeNode.eulerAngles.x = -.pi / 2
// 6
node.addChildNode(planeNode)
}
Let me walk you through the code line by line:
x
, y
, and z
constants to represent the planeAnchor
’s center x, y, and z position. This is for our planeNode
’s position. We rotate the planeNode
’s x euler angle by 90 degrees in the counter-clockerwise direction, else the planeNode
will sit up perpendicular to the table. And if you rotate it clockwise, David Blaine will perform a magic illusion because SceneKit renders the SCNPlane surface using the material from one side by default.planeNode
as the child node onto the newly added SceneKit node.Build and run the project. You should now be able to detect and visualize the detected horizontal plane.
With ARKit receiving additional information about our environment, we may want to expand our previously detected horizontal plane(s) to make use of a larger surface or have a more accurate representation with the new information.
Hence, implement renderer(_:didUpdate:for:)
:
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
}
--- Missing Instruction [End] ---
This method gets called every time a SceneKit node's properties have been updated to match its corresponding anchor. This is where ARKit refines its estimation of the horizontal plane's position and extent.
The node
argument gives us the updated position of the anchor. The anchor
argument provides us with the anchor's updated width and height. With these two arguments, we can update the previously implemented SCNPlane to reflect the updated position with the updated width and height.
. . .
Chapter 6 Section: Detecting horizontal planes. The guard statement always returns because of multile reasons. 1) the Node for the didAdd call does not have any child nodes. 2) the node for the didAdd call does not have any geometry and could not be cast to a SCNPLane.
A bit of code is missing for vizualing a plane on a plane anchor. Apple Documentation has an example here (https://developer.apple.com/documentation/arkit/content_anchors/tracking_and_visualizing_planes) that may be useful, but the book and source code definitely needs to be updated if that was the intention of this section of the chapter.