My goal is to calculate the "visible" part of vertical plane that is anchored to some ARPlaneAnchor
and represent it with quadrangle as shown in the picture below:
My current approach is based on few hit tests, which unfortunately seems to not giving me the satisfying results.
First, when I detect a ARPlaneAnchor
I add a big invisible SCNNode
to its main node.
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
let planeNode = SCNNode(geometry: SCNPlane(width: 100.0, height: 100.0))
planeNode.name = "infinite_plane"
planeNode.position = SCNVector3(planeAnchor.center.x, planeAnchor.center.y, planeAnchor.center.z)
planeNode.eulerAngles = SCNVector3(-0.5 * .pi, 0, 0)
planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.clear
node.addChildNode(planeNode)
node.enumerateChildNodes { childNode, _ in
childNode.removeFromParentNode()
}
}
Then, when it's time to calculate the visible part of vertical plane (it doesn't matter when, it can be after some screen touch or each time the new frame is being rendered) I take the center of the screen and perform some hit tests.
let center = CGPoint(x: sceneView.bounds.midX, y: sceneView.bounds.midY)
let hitTestResults = sceneView.hitTest(center, types: [.existingPlaneUsingGeometry, .estimatedVerticalPlane])
if let hitTestResult = hitTestResults.first,
let hitAnchor = hitTestResult.anchor,
let hitNode = sceneView.node(for: hitAnchor) {
let width = sceneView.bounds.width
let height = sceneView.bounds.height
// A B
// C D
guard let aPosition = sceneView.hitTest(CGPoint(x: 0, y: 0)).first?.worldCoordinates,
let bPosition = sceneView.hitTest(CGPoint(x: width, y: 0)).first?.worldCoordinates,
let cPosition = sceneView.hitTest(CGPoint(x: 0, y: height)).first?.worldCoordinates,
let dPosition = sceneView.hitTest(CGPoint(x: width, y: height)).first?.worldCoordinates else {
return
}
let geometry = SCNGeometry.polygon(vertices: [aPosition, bPosition, dPosition, cPosition])
let node = SCNNode(geometry: geometry)
node.geometry?.firstMaterial?.diffuse.contents = UIColor.green
node.geometry?.firstMaterial?.isDoubleSided = true
sceneView.scene.rootNode.addChildNode(node)
}
extension SCNGeometry {
static func polygon(vertices: [SCNVector3]) -> SCNGeometry {
let source = SCNGeometrySource(vertices: vertices)
var indices = vertices.indices.map { UInt32($0) }
indices.insert(UInt32(vertices.count), at: 0)
let data = Data(bytes: indices, count: indices.count * MemoryLayout<Int32>.size)
let element = SCNGeometryElement(
data: data,
primitiveType: .polygon,
primitiveCount: 1,
bytesPerIndex: MemoryLayout<Int32>.size
)
return SCNGeometry(sources: [source], elements: [element])
}
}
And here I stopped. The drawn polygon doesn't seem to be representing the visible part of the vertical plane.
The question is, how where my estimatations are wrong and how should I detect the "visible" part of vertical plane correctly?
Try this approach:
extension ViewController: ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer,
didAdd node: SCNNode,
for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor
else { return }
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
let myPlane = SCNPlane(width: width,
height: height)
myPlane.materials.first?.diffuse.contents = UIColor(white: 0.0,
alpha: 0.5)
let planeNode = SCNNode(geometry: myPlane)
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 = -Float.pi / 2
node.addChildNode(planeNode)
}
func renderer(_ renderer: SCNSceneRenderer,
didUpdate node: SCNNode,
for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor,
let planeNode = node.childNodes.first,
let myPlane = planeNode.geometry as? SCNPlane
else { return }
let width = CGFloat(planeAnchor.extent.x)
let height = CGFloat(planeAnchor.extent.z)
myPlane.width = width
myPlane.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)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With