Поместите несколько объектов SCN в метод touchesBegan - PullRequest
2 голосов
/ 06 февраля 2020

Мой код Swift ниже использует fun c touchesBegan для размещения объекта SCN в представлении ARKit. Проблема в том, что объект размещается только один раз. Я хотел бы создать код таким образом, чтобы пользователи могли выбирать любую область для размещения объекта SCN, и он может размещать ее столько раз, сколько они захотят.

Вот GitHub ссылка .

enter image description here

class ViewController: UIViewController, ARSCNViewDelegate { 

    @IBOutlet var sceneView: ARSCNView! 

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        // Handle the shooting
        guard let frame = sceneView.session.currentFrame else { return }
        let camMatrix = SCNMatrix4(frame.camera.transform)

        let direction = SCNVector3Make(camMatrix.m31 * 5.0, 
                                       camMatrix.m32 * 10.0, 
                                       camMatrix.m33 * 5.0)

        let position = SCNVector3Make(camMatrix.m41, camMatrix.m42, camMatrix.m43)
        let scene = SCNScene(named: "art.scnassets/dontCare.scn")!
        sceneView.scene = scene
    }    
}  

enter image description here

Ответы [ 2 ]

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

Решение 1. Добавьте 3D-модели, используя func touchesBegan(:with:)

Используйте следующий код для получения желаемого эффекта (поместите столько объектов в сцену, сколько хотите):

enter image description here

Сначала создайте расширение для вашего удобства:

import ARKit

extension SCNVector3 {

    static func + (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 {
        return SCNVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z)
    }
}

Затем используйте его в вашем ViewController для добавления pointOfView.position в desiredVector:

class ViewController: UIViewController {

    @IBOutlet var sceneView: ARSCNView!

    override func touchesBegan(_ touches: Set<UITouch>,
                              with event: UIEvent?) {

        sceneView.isMultipleTouchEnabled = true

        guard let pointOfView = sceneView.pointOfView   // ARCamera of SCNScene 
        else { return }

        let cameraMatrix = pointOfView.transform

        let desiredVector = SCNVector3(cameraMatrix.m31 * -0.5,
                                       cameraMatrix.m32 * -0.5,
                                       cameraMatrix.m33 * -0.5)

        // What the extension SCNVector3 is for //
        let position = pointOfView.position + desiredVector 

        let sphereNode = SCNNode()
        sphereNode.geometry = SCNSphere(radius: 0.05)
        sphereNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green
        sphereNode.position = position
        sceneView.scene.rootNode.addChildNode(sphereNode)
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let scene = SCNScene(named: "art.scnassets/myScene.scn")!
        sceneView.scene = scene
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let config = ARWorldTrackingConfiguration()
        sceneView.session.run(config)
    }
}

И если вы хотите извлечь 3D-модель из файла .scn, используйте следующий код :

(вместо sphereNode):

var modelNode = SCNNode()
let myScene = SCNScene(named: "art.scnassets/ship.scn")

// Model's name in a Scene graph hierarchy.
// Pay particular attention – it's not a name of .scn file.
let nodeName = "ship"

modelNode = (myScene?.rootNode.childNode(withName: nodeName, recursively: true))!
modelNode.position = position
sceneView.scene.rootNode.addChildNode(modelNode)

enter image description here

Решение 2. Добавление 3D-моделей с использованием определения плоскости и проверки удара

Используйте следующий код, если вы хотите добавить модели, используя обнаружение самолета и тестирование Hit:

Сначала создайте расширение для вашего удобства:

extension float4x4 {

    var simdThree: SIMD3<Float> {
        let translation = self.columns.3            
        return SIMD3<Float>(translation.x, translation.y, translation.z)
    }
}

Затем используйте его в основном коде:

class ViewController: UIViewController {

    @IBOutlet weak var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        addGesture()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        sceneView.delegate = self                  // for ARSCNViewDelegate

        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal]
        sceneView.session.run(config)
    }

    func addGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, 
                                                action: #selector(ViewController.addModel))
        sceneView.addGestureRecognizer(tapGesture)
    }

    // Don't forget to drag-and-drop TapGestureRecognizer object from library
    @objc func addModel(recognizer: UIGestureRecognizer) {

        let tap: CGPoint = recognizer.location(in: sceneView)

        let results: [ARHitTestResult] = sceneView.hitTest(tap, 
                                                    types: .existingPlaneUsingExtent)

        guard let hitTestResult = results.first 
        else { return }

        let translation = hitTestResult.worldTransform.simdThree
        let x = translation.x
        let y = translation.y
        let z = translation.z

        guard let scene = SCNScene(named: "art.scnassets/myScene.scn"),
              let robotNode = scene.rootNode.childNode(withName: "robot", 
                                                    recursively: true)
        else { return }

        robotNode.position = SCNVector3(x, y, z)
        robotNode.scale = SCNVector3(0.02, 0.02, 0.02)
        sceneView.scene.rootNode.addChildNode(robotNode)
    }
}

И вам нужно реализовать логику c внутри двух renderer() методов для ARPlaneAnchor s:

extension ViewController: ARSCNViewDelegate {

    func renderer(_ renderer: SCNSceneRenderer,
                 didAdd node: SCNNode,
                  for anchor: ARAnchor) { // your logic here....  }

    func renderer(_ renderer: SCNSceneRenderer,
              didUpdate node: SCNNode,
                  for anchor: ARAnchor) { // your logic here....  }

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

'объект' - это SCNNode. Они отображаются в 3D-сцене, SCNScene. каждый раз, когда вы нажимаете на экран, вы применяете сцену к sceneView вместо добавления узла к сцене.

Вам также необходимо определить, где пользователь коснулся сцены, ie 3D-положение касания. Это требовало теста на удар.

попробуйте

class ViewController: UIViewController, ARSCNViewDelegate {

@IBOutlet var sceneView: ARSCNView!

override func viewDidLoad() {
    super.viewDidLoad()
    sceneView.delegate = self
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let configuration = ARWorldTrackingConfiguration()
    sceneView.session.run(configuration)
}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard
        let touchPosition = touches.first?.location(in: sceneView),
        let hitTest = sceneView.hitTest(touchPosition, types: .featurePoint).first
        else {return}

    let node = SCNScene(named: "art.scnassets/dontCare.scn")!.rootNode.childNodes.first!
    node.simdTransform = hitTest.worldTransform
    sceneView.scene.rootNode.addChildNode(node)
}

}
...