Search code examples
scenekitarkitscnnodearanchor

How to place SCNFloor on real floor?


I'd like to add an scnfloor where the real floor is, but it appears above and when adding a childNode, the object doesn't lay on the floor, but looks like flying.

When logging the position of the added node or plane anchor I always get y = 0, no matter if I'm scanning the floor or a table.

What am I missing? Or is it impossible to change the y of a scnfloor? But in the docs is written

"...of its local coordinate space"

, so I thought it was possible.

Thanks

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    guard let planeAnchor = anchor as? ARPlaneAnchor else {return}
    let anchorX = planeAnchor.center.x
    let anchorY = planeAnchor.center.y
    let anchorZ = planeAnchor.center.z

    print("planeAnchor.center %@", planeAnchor.center) //y always 0
    print("node.position %@", node.position) //y always 0
    print("convert %@", node.convertPosition(SCNVector3(anchorX, anchorY, anchorZ), to: nil)) //y always 0

    let floor = SCNFloor()
    let floorNode = SCNNode(geometry: floor)
    floorNode.position = SCNVector3(anchorX, anchorY, anchorZ)
    let floorShape = SCNPhysicsShape(geometry: floor, options: nil)
    floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: floorShape)
    node.addChildNode(floorNode)
    let childNode = createChildNode()
    floorNode.addChildNode(childNode)
}

Solution

  • The center.y value of the anchor will always be zero, there is some more information here: https://developer.apple.com/documentation/arkit/arplaneanchor/2882056-center

    Basically the anchors transform will give the correct position of the detected plane instead. Because of this you can just add your SCNFloor instance as a child of the passed in node and it will render in the correct position, there is no need to set the position explicitly as you are doing above.

    You will need to update your geometry size as the ARPlaneAnchor's extent changes as ARKit refines the bounds of the plane. Some more information is here: https://developer.apple.com/documentation/arkit/tracking_and_visualizing_planes

    For example, the code below works for me, rendering a SCNFloor at the initially detected size of the plane and renders a box that will sit exactly on top of it. The plane is accurately placed on the floor (or whatever horizontal surface you detect):

      func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let planeAnchor = anchor as? ARPlaneAnchor else {
          return
        }
    
        let floor = SCNFloor()
        let floorNode = SCNNode(geometry: floor)
        floor.length = CGFloat(planeAnchor.extent.z)
        floor.width = CGFloat(planeAnchor.extent.x)
        node.addChildNode(floorNode)
    
        let box = SCNBox(width: 0.05, height: 0.05, length: 0.05, chamferRadius: 0)
        box.materials[0].diffuse.contents = UIColor.red
        let boxNode = SCNNode(geometry: box)
        boxNode.position = SCNVector3(0, 0.025, 0)
    
        node.addChildNode(boxNode)
      }