Swift -How to update ARAnchor to follow Camera's position

霸气de小男生 提交于 2020-03-04 19:35:15

问题


I followed this code from @rickster which 100% works and looks great. In the video he's animating a SCNNode that is set using an ARAnchor from 1 position to another and back. I tried to do something similar except I want the node that is set with the ARAnchor to follow/update it's position to another node that is a child of the camera.

I'm having a problem updating the position in func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) { }

I tried to animate the node that is set with the ARAnchor to follow the other node but it's not working, it follows backwards and in reverse:

let animation = CABasicAnimation(keyPath: #keyPath(SCNNode.transform))
animation.fromValue = nodeSetWithARAnchor.transform
animation.toValue = nodeTiedToCamera.transform
animation.duration = 1
nodeSetWithARAnchor.removeAllAnimations()
nodeSetWithARAnchor.addAnimation(animation, forKey: nil)

I then tried to remove the ARAnchor and reset its node's .worldPostion and .simdWorldTransform but the node diappears. It's in steps 7 & 8 below.

How can I get the nodeSetWithARAnchor to update its ARAnchor and position to always follow the nodeTiedToCamera?

Update In Step 6 now that I set the nodeSetWithARAnchor SCVector3 to match the nodeTiedToCameradWorld's SCVector3 and set its .transform to match the nodeTiedToCameradWorldTransform @rickster's animation code works the best because I don't I have to remove any anchors. There is another problem though. The nodeSetWithARAnchor responds when I move the device but it responds backwards and in reverse.

When I turn the device up the image goes right and when I turn the device down the image goes left. When I turn the device left the image goes up and when I turn the device right the image goes down. It's following the image I have tied to the camera but it's following it incorrectly.

let configuration = ARWorldTrackingConfiguration()

var nodeSetWithARAnchor: SCNNode?
var nodeTiedToCamera: SCNNode?
var anchors: [ARAnchor] = []

override func viewDidLoad() {
    super.viewDidLoad()

    configuration.planeDetection = [.horizontal, .vertical]
    configuration.maximumNumberOfTrackedImages = 1

    // 1. once this anchor is set inside renderer(_:didAdd:for:) I initialize the nodeSetWithARAnchor at 30cm behind the device's camera's initial position
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        var translation = matrix_identity_float4x4
        translation.columns.3.z = -0.3
        let transform = simd_mul(self.sceneView.session.currentFrame!.camera.transform, translation)
        let anchor = ARAnchor(transform: transform)
        self.sceneView.session.add(anchor: anchor)
    }

    // 2. the nodeTiedToCamera will always go where ever the device's camera goes
    let plane = SCNPlane(width: 0.1, height: 0.1)
    nodeTiedToCamera = SCNNode(geometry: plane)
    nodeTiedToCamera!.position = SCNVector3(x: -0.15, y: 0.45, z: -1.25) // I don't want it directly in front of the camera
    nodeTiedToCamera!.geometry?.fisrtMaterial?.diffuse.contents = UIColor.red
    sceneView.pointOfView.addChildNode(nodeTiedToCamera!)
}

// 3. I init the nodeSetWithARAnchor, add it to the sceneView's root node, and keep a copy of it's anchor
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

    DispatchQueue.main.async {

        if self.nodeSetWithARAnchor == nil {
            // create geometry ...
            self.nodeSetWithARAnchor = SCNNode(geometry: geometry)

            node.addChildNode(self.nodeSetWithARAnchor!)
        }

        self.anchors.removeAll()
        self.anchors.append(anchor)
    }
}

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {

    DispatchQueue.main.async {

        // 4. get the only child that is tied to the camera which is the nodeTiedToCamera
        guard let pointOfView = self.sceneView.pointOfView else { return }
        guard let child = pointOfView.childNodes.first else { return }

        // 5. get it's .worldPosition && it's .simdWorldTransform
        let nodeTiedToCameradWorldPosition = child.worldPosition
        let nodeTiedToCameradWorldTransform = child.worldTransform

        if let nodeSetWithARAnchor = self.nodeSetWithARAnchor, let anchorToRemove = self.anchors.first {

             // 6. set the nodeSetWithARAnchor SCVector3 to match the nodeTiedToCameradWorld's SCVector3 and set its .transform to match the nodeTiedToCameradWorldTransform
             nodeSetWithARAnchor.position = nodeTiedToCameradWorldPosition
             nodeSetWithARAnchor.transform = nodeTiedToCameradWorldTransform

             let animation = CABasicAnimation(keyPath: #keyPath(SCNNode.transform))
             animation.fromValue = nodeSetWithARAnchor.transform
             animation.toValue = nodeTiedToCamera.transform
             animation.duration = 1
             nodeSetWithARAnchor.removeAllAnimations()
             nodeSetWithARAnchor.addAnimation(animation, forKey: nil)

             // 7. remove all ARAnchors
             //self.sceneView.session.remove(anchor: anchorToRemove)
             //self.anchors.removeAll()

             // 8. add a new anchor to the session and set it with the nodeSetWithARAnchor.simdWorldTransform
            //let anchor = ARAnchor(transform: nodeSetWithARAnchor.simdWorldTransform)
            //self.sceneView.session.add(anchor: anchor)
        }
    }
}

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    guard let node = self.nodeSetWithARAnchor else { return }

    if let pointOfView = sceneView.pointOfView {
        let isVisible = sceneView.isNode(node, insideFrustumOf: pointOfView)
        print("Is node visible: \(isVisible)")
    }
}

回答1:


It was an ALL day thing but got it working. I only had to switch around 2 lines of code in renderer(:willRenderScene:atTime:) and run them in the exact order below. I didn't have to remove and add anchors or run any animation code.

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {

    DispatchQueue.main.async { [weak self] in

        guard let safeSelf = self else { return }
        guard let pointOfView = safeSelf.sceneView.pointOfView else { return }
        guard let child = pointOfView.childNodes.first else { return } // child is the nodeTiedToCamera

        if let nodeSetWithARAnchor = safeSelf.nodeSetWithARAnchor {

            // *** I just had switch around these 2 lines of code and run them in this exact order ***
            nodeSetWithARAnchor.transform = child.worldTransform
            nodeSetWithARAnchor.worldPosition = child.worldPosition
        }
    }
}


来源:https://stackoverflow.com/questions/60327523/swift-how-to-update-aranchor-to-follow-cameras-position

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!