Swift -Как обновить ARAnchor, чтобы следовать положению камеры - PullRequest
0 голосов
/ 20 февраля 2020

Я следовал этому коду от @rickster, который работает на 100% и выглядит отлично . В видео он анимирует SCNNode, который устанавливается с помощью ARAnchor из одной позиции в другую и обратно. Я пытался сделать что-то подобное, за исключением того, что я хочу, чтобы узел, установленный с помощью ARAnchor, следовал / обновлял свою позицию для другого узла, который является дочерним элементом камеры.

У меня проблема с обновлением позиции в func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) { }

Я пытался анимировать узел, для которого установлен ARAnchor, чтобы следовать за другим узлом, но он не работает, он следует назад и в обратном направлении:

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)

Затем я попытался удалить ARAnchor и сбросить его узлы .worldPostion и .simdWorldTransform, но узлы появляются. Это в шагах 7 и 8 ниже.

Как мне заставить nodeSetWithARAnchor обновить свой ARAnchor и положение, чтобы всегда следовать nodeTiedToCamera?

Обновление На шаге 6 теперь, когда я установил nodeSetWithARAnchor SCVector3 для соответствия nodeTiedToCameradWorld's SCVector3 и установил .transform для соответствия коду анимации nodeTiedToCameradWorldTransform @ rickster, лучше всего работает, потому что мне не нужно удалять любые якоря. Есть еще одна проблема. nodeSetWithARAnchor реагирует, когда я перемещаю устройство, но реагирует в обратном и обратном направлениях.

Когда я поднимаю устройство вверх, изображение идет вправо, а когда я выключаю устройство, изображение идет влево. Когда я поворачиваю устройство влево, изображение увеличивается, а когда я поворачиваю устройство вправо, изображение уменьшается. Он следует за изображением, которое я привязал к камере, но неверно.

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 Ответ

0 голосов
/ 21 февраля 2020

Это был ВЕСЬ день, но он заработал. Мне нужно было всего лишь переключить 2 строки кода в renderer(:willRenderScene:atTime:) и запустить их в точном порядке ниже. Мне не нужно было удалять и добавлять якоря или запускать любой код анимации.

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
        }
    }
}
...