I have a simple plane node that tracks a face.
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
guard anchor is ARFaceAnchor else { return nil }
let plane = SCNPlane(width: 0.1, height: 0.2)
let planeNode = SCNNode(geometry: plane)
planeNode.geometry?.firstMaterial?.fillMode = .lines
planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
planeNode.name = "Plane Node"
return planeNode
}
I want to be able to track the coordinates of all four corners of the plane. I'm looking to get the 2D coordinates that are projected on the screen.
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let faceAnchor = anchor as? ARFaceAnchor else {
return
}
node.enumerateChildNodes { childNode, _ in
guard childNode.name == "Plane Node" else { return }
let worldPosition = childNode.worldPosition
let screenPosition = renderer.projectPoint(worldPosition)
print(CGPoint(x: Int(screenPosition.x), y: Int(screenPosition.y)))
}
}
Above tracks the center position of the plane, but how do I track the four corner coordinates?
I tried using the width and the height of the plane using the following to calculate the distance from the center coordinate, but I'm unable to get the proper width and the height that I can work with screen position I've obtained for the center coordinate.
extension SCNNode {
var width: Float {
return (boundingBox.max.x - boundingBox.min.x) * scale.x
}
var height: Float {
return (boundingBox.max.y - boundingBox.min.y) * scale.y
}
}
CodePudding user response:
It could be probably a solution (woraround) to add i.Ex. invisible childNodes
(without geometry) to each of the corners or edges which positions you want to track. Then grab the presentation.worldPosition
of each of those nodes
at anytime you like, and convert them using your function above to 2D space.
CodePudding user response:
Try the following solution. Despite the fact that it's a macOS project, you can easily implement this idea in your ARKit project.
import SceneKit
class ViewController: NSViewController {
var sceneView: SCNView? = nil
let planeNode = SCNNode()
var vertices: [SCNVector3] = []
override func viewDidLoad() {
super.viewDidLoad()
sceneView = self.view as? SCNView
sceneView?.scene = SCNScene()
sceneView?.delegate = self
sceneView?.backgroundColor = .black
sceneView?.autoenablesDefaultLighting = true
// Plane
let plane = SCNPlane(width: 0.5, height: 0.5)
planeNode.name = "planar"
planeNode.geometry = plane
planeNode.geometry?.firstMaterial?.isDoubleSided = true
sceneView?.scene?.rootNode.addChildNode(planeNode)
planeNode.runAction(SCNAction.move(to: SCNVector3(0.2, 0.2,-0.2),
duration: 2))
self.trackingSpheres()
}
}
Delegate's method (in your case it's renderer(_:didUpdate:for:)
instance method).
extension ViewController: SCNSceneRendererDelegate {
func renderer(_ renderer: SCNSceneRenderer,
updateAtTime time: TimeInterval) {
if let spheres = sceneView?.scene?.rootNode.childNodes[0].childNodes {
for (_, sphere) in spheres.enumerated() {
let truncateX = String(format: "%.2f", sphere.worldPosition.x)
let truncateY = String(format: "%.2f", sphere.worldPosition.y)
let truncateZ = String(format: "%.2f", sphere.worldPosition.z)
print("\(sphere.name!):", truncateX, truncateY, truncateZ)
}
}
}
}
A method creating four invisible tiny tracking spheres.
extension ViewController {
fileprivate func trackingSpheres() {
// retrieving a plane node from scene
if let node = sceneView?.scene?.rootNode.childNode(
withName: "planar",
recursively: true) {
let left = node.boundingBox.min.x
let right = node.boundingBox.max.x
let lower = node.boundingBox.min.y
let upper = node.boundingBox.max.y
let south = node.boundingBox.min.z
// Counter clock-wise
let ll = SCNVector3(x: left, y: lower, z: south)
let lr = SCNVector3(x: right, y: lower, z: south)
let ur = SCNVector3(x: right, y: upper, z: south)
let ul = SCNVector3(x: left, y: upper, z: south)
self.vertices = [ll, lr, ur, ul]
for i in 1...4 {
let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.01))
sphereNode.position = SCNVector3(vertices[i-1].x,
vertices[i-1].y,
vertices[i-1].z)
sphereNode.name = "sphere\(i)"
sphereNode.opacity = 0.0 // 100% transparent
sphereNode.geometry?.firstMaterial?.diffuse.contents =
NSColor.red
node.addChildNode(sphereNode)
}
}
}
}