Удалить текстовый узел при сбросе сеанса в ARKit 1.5 - PullRequest
0 голосов
/ 25 апреля 2018

Я немного изменил Пример Apple о распознавании изображений в ARKit 1.5, чтобы каждый раз, когда изображение распознавалось, я отображал некоторую текстовую геометрию перед ним.

К сожалению, я заметил, что когдаПри нажатии кнопки сброса в представлении StatusView геометрия моего текста не удаляется, а только перемещается где-то вокруг нового центра сцены.

Вместо этого, если я нажимаю кнопку возврата (потому что я встроил контроллер основного видав контроллере навигации, чтобы запустить сеанс AR с помощью кнопки, а не автоматически, как в примере), и снова запустить сеанс, затем все исчезает правильно.

Анализируя код, я могу подтвердить, что обакнопка сброса и переход назад и вперед из ViewController запускают ту же функцию, как показано ниже

func resetTracking() {

    guard let referenceImages = loadImageReferences() else {
        fatalError("Missing expected asset catalog resources.")
    }

    let configuration = ARWorldTrackingConfiguration()
    configuration.detectionImages = referenceImages
    configuration.planeDetection = [.horizontal, .vertical]
    session.run(configuration, options: [.resetTracking, .removeExistingAnchors])

    statusViewController.scheduleMessage("Look around to detect images", inSeconds: 7.5, messageType: .contentPlacement)
}

Как я уже говорил, эта функция вызывается либо в ViewDidLoad методе ViewController

override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.delegate = self
    sceneView.session.delegate = self

    // debug scene to see feature points and world's origin ARSCNDebugOptions.showFeaturePoints,
    self.sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin]

    // Hook up status view controller callback(s).
    statusViewController.restartExperienceHandler = { [unowned self] in
        self.restartExperience()
    }   
}

func restartExperience() {
    guard isRestartAvailable else { return }
    isRestartAvailable = false

    statusViewController.cancelAllScheduledMessages()

    resetTracking()

    // Disable restart for a while in order to give the session time to restart.
    DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
        self.isRestartAvailable = true
    }
}

, а также после нажатия кнопки сброса в StatusViewController.

@IBAction private func restartExperience(_ sender: UIButton) {
    restartExperienceHandler()
}

var restartExperienceHandler: () -> Void = {}

для дальнейшей справки, я могу создать следующий текст

func createBillboardText(text: String, position: SCNNode, width: CGFloat, height: CGFloat){

    let newText = splitString(every: 10, string: text)
    let textGeometry = SCNText(string: newText, extrusionDepth: 1.0)

    textGeometry.firstMaterial!.diffuse.contents = UIColor.red
    let textNode = SCNNode(geometry: textGeometry)

    // Update object's pivot to its center
    // https://stackoverflow.com/questions/44828764/arkit-placing-an-scntext-at-a-particular-point-in-front-of-the-camera
    let (min, max) = textGeometry.boundingBox
    let dx = min.x + 0.5 * (max.x - min.x)
    let dy = min.y + 0.5 * (max.y - min.y)
    let dz = min.z + 0.5 * (max.z - min.z)
    textNode.pivot = SCNMatrix4MakeTranslation(dx, dy, dz)
    textNode.scale = SCNVector3(0.005, 0.005, 0.005)

    let plane = SCNPlane(width: width, height: height)
    let blueMaterial = SCNMaterial()
    blueMaterial.diffuse.contents = UIColor.blue
    blueMaterial.transparency = 0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    plane.firstMaterial = blueMaterial
    let parentNode = SCNNode(geometry: plane) // this node will hold our text node

    let yFreeConstraint = SCNBillboardConstraint()
    yFreeConstraint.freeAxes = [.Y] // optionally
    parentNode.constraints = [yFreeConstraint] // apply the constraint to the parent node

    parentNode.addChildNode(textNode)

    position.addChildNode(parentNode)

    sceneView.autoenablesDefaultLighting = true

}

, вызываемый в методе func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) как self.createBillboardText(text: message, position: node, width: referenceImage.physicalSize.width, height: referenceImage.physicalSize.height )

Я также нашел этот поток но я понятия не имею, как вызвать эту функцию на сцене.

Ответы [ 2 ]

0 голосов
/ 25 апреля 2018

Если вы хотите удалить все узлы, используйте это:

self.sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
    node.removeFromParentNode()
}

Теперь, если вы хотите удалить конкретный узел, используйте это:

self.sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
        if node.name == "textNode" {
            node.removeFromParentNode()
        }
}

Это также работает на SCNNodeи не только ARSCNView, что означает, что вы можете перебирать дочерние узлы до SCNNode, а не только из сцен rootNode

Это код, который я использую в проекте.Здесь я сначала удаляю все childNodes на узле, а затем удаляю сам SCNNode.Затем я продолжаю приостанавливать сеанс, который завершит его.Я запускаю новый сеанс и, наконец, сбрасываю worldOrigin в текущую позицию камеры.Поскольку формы могут получить неправильный угол, если угол запуска приложения выключен.

sceneView.scene.rootNode.enumerateHierarchy { (node, stop) in
       node.childNodes.forEach({
          $0.removeFromParentNode()
       })
       node.removeFromParentNode()
}
sceneView.session.pause()
sceneView.session.run(configuration, options: .resetTracking)
sceneView.session.setWorldOrigin(relativeTransform: matrix)
0 голосов
/ 25 апреля 2018

Я считаю, что способ, которым вы хотите обработать удаление вашего SCNNodes, заключается в вызове следующей функции в вашем resetFunction:

self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in
    existingNode.removeFromParentNode()
}

, где augmentedRealityView относится к ARSCNView.

Вы также можете хранить определенные узлы в массиве [SCNNode], а затем проходить через них, чтобы удалить их, например:

for addedNode in nodesAdded{

addedNode.removeFromParentNode()

}

Надеюсь, это поможет ...

...