3D-модель Collide RealityKit с сеткой LiDAR

Я провел много дней, безуспешно пытаясь понять примеры и следовать им. Моя цель - поместить виртуальный объект AR в реальный мир, отсканированный ранее с помощью LiDAR. С showSceneUnderstanding я могу видеть, что сетка в реальном времени создана нормально. С помощью функции касания я могу вставить файл usdz, это тоже нормально. Поскольку у меня есть toyModel.physicsBody?.mode = .kinematic и self.arView.installGestures(for: toyRobot), я могу перемещать / масштабировать модель. Теперь нужно иметь возможность перемещать модель И сталкиваться с сеткой, созданной LiDAR. Когда я перемещаю модель к отсканированной стене, например, сетка останавливается.

Вот мой полный код:

import UIKit
import RealityKit
import ARKit

class ViewController: UIViewController, ARSessionDelegate {
    
    @IBOutlet var arView: ARView!
    var tapRecognizer = UITapGestureRecognizer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.arView.session.delegate = self
        
        //Scene Understanding options
        self.arView.environment.sceneUnderstanding.options.insert([.physics, .collision, .occlusion])
        
        //Only for dev
        self.arView.debugOptions.insert(.showSceneUnderstanding)
        
        self.tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(placeObject(_:)))
        self.arView.addGestureRecognizer(self.tapRecognizer)
        
    }
    
    @objc func placeObject(_ sender: UITapGestureRecognizer) {
        
        // Perform a ray cast against the mesh (sceneUnderstanding)
        // Note: Ray-cast option ".estimatedPlane" with alignment ".any" also takes the mesh into account.
        let tapLocation = sender.location(in: arView)
        if let result = arView.raycast(from: tapLocation, allowing: .estimatedPlane, alignment: .any).first {
            
            // Load the "Toy robot"
            let toyRobot = try! ModelEntity.loadModel(named: "toy_robot_vintage.usdz")
                
            // Add gestures to the toy (only available is physicsBody mode == kinematic)
            self.arView.installGestures(for: toyRobot)
            
            // Toy Anchor to place the toy on surface
            let toyAnchor = AnchorEntity(world: result.worldTransform)
                toyAnchor.addChild(toyRobot)
            
            // Create a "Physics" model of the toy in order to add physics mode
            guard let toyModel = toyAnchor.children.first as? HasPhysics else {
                return
            }
            
            // Because toyModel is a fresh new model we need to init physics
            toyModel.generateCollisionShapes(recursive: true)
            toyModel.physicsBody = .init()
            
            // Add the physics body mode
            toyModel.physicsBody?.mode = .kinematic
            
            let test = ShapeResource.generateConvex(from: toyRobot.model!.mesh)
            
            toyModel.components[CollisionComponent] = CollisionComponent(shapes: [test], mode: .default, filter: .default)
            
            // Finally add the toy anchor to the scene
            self.arView.scene.addAnchor(toyAnchor)
        }
        
    }
}

Кто-нибудь знает, можно ли этого добиться? Спасибо заранее!


person Silvering    schedule 04.11.2020    source источник
comment
Привет, @AndyFedoroff, спасибо за ответ. Я имею в виду, если больше знает, как остановить 3D-модель AR, когда я перетаскиваю ее, например, на стену, сканированную лидаром.   -  person Silvering    schedule 04.11.2020
comment
@AndyFedoroff да именно !!   -  person Silvering    schedule 04.11.2020
comment
@AndyFedoroff, может быть, у вас есть идея ;-)? Спасибо за поддержку!   -  person Silvering    schedule 04.11.2020
comment
Ухаооо супер !! Не могу дождаться вашего ответа ;-)   -  person Silvering    schedule 04.11.2020


Ответы (2)


Попробуйте понизить до Entity & HasPhysics:

guard let model = anchor.children.first as? (Entity & HasPhysics) 
else { return }

model.generateCollisionShapes(recursive: true)
model.physicsBody = PhysicsBodyComponent(shapes: [.generateBox(size: .one)],
                                           mass: 1.0, 
                                       material: .default, 
                                           mode: .kinematic)

В официальной документации говорится:

Для немодельных объектов generateCollisionShapes(recursive:) метод не действует. Тем не менее, метод определен для всех сущностей, поэтому вы можете вызывать его для любой сущности и рекурсивно распространять вычисления на всех потомков этой сущности.

person Andy Fedoroff    schedule 04.11.2020
comment
Привет, @AndyFedoroff, спасибо за ответ. Вот мой обновленный код, содержащий ваше решение: gist.github.com/MickaelCruzDeeB/047c136a432d 3D-модель может пересекать стены и все препятствия, созданные лидаровой сеткой. - person Silvering; 05.11.2020
comment
Более того, я не могу принудительно выполнить приведение без этой ошибки: Инициализатор для условной привязки должен иметь тип Optional, а не Entity & HasPhysics. - person Silvering; 05.11.2020
comment
Я нашел ваш ответ в этой теме: stackoverflow.com/questions/62393887/ может быть идея выпуклого луча ... Как вы думаете? - person Silvering; 05.11.2020
comment
Да, использовать raycast - хорошая идея. Жалко, но протестировать не могу, потому что на данный момент у меня нет устройства с LiDAR ... - person Andy Fedoroff; 05.11.2020
comment
Нет проблем, но не могли бы вы мне помочь с кодом? Я не понимаю, как реализовать конвекстный raycast с моим реальным кодом? Спасибо еще раз!! - person Silvering; 05.11.2020

Следуя предыдущему обсуждению с @AndyFedoroff, я добавил конвекстный raycast для того, чтобы всегда сталкивать размещенный трехмерный объект с сеткой, созданной LiDAR. Вот мой полный код. Не знаю, хорошо ли у меня дела ... В любом случае все равно не получается.

import UIKit
import RealityKit
import ARKit

class ViewController: UIViewController, ARSessionDelegate {
    
    @IBOutlet var arView: ARView!
    
    var tapRecognizer = UITapGestureRecognizer()
    var panGesture = UIPanGestureRecognizer()
    var panGestureEntity: Entity? = nil
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.arView.session.delegate = self
        
        //Scene Understanding options
        self.arView.environment.sceneUnderstanding.options.insert([.physics, .collision, .occlusion])
        
        //Only for dev
        self.arView.debugOptions.insert(.showSceneUnderstanding)
        
        self.tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.placeObject))
        self.arView.addGestureRecognizer(self.tapRecognizer)
        
        self.panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didPan))
        self.arView.addGestureRecognizer(self.panGesture)
    }
    
    @objc func placeObject(_ sender: UITapGestureRecognizer) {
        
        // Perform a ray cast against the mesh (sceneUnderstanding)
        // Note: Ray-cast option ".estimatedPlane" with alignment ".any" also takes the mesh into account.
        let tapLocation = sender.location(in: arView)
        if let result = self.arView.raycast(from: tapLocation, allowing: .estimatedPlane, alignment: .any).first {

            // Load the "Toy robot"
            let toyRobot = try! ModelEntity.loadModel(named: "toy_robot_vintage.usdz")

            // Add gestures to the toy (only available is physicsBody mode == kinematic)
            self.arView.installGestures(for: toyRobot)

            // Toy Anchor to place the toy on surface
            let toyAnchor = AnchorEntity(world: result.worldTransform)
                toyAnchor.addChild(toyRobot)

            // Create a "Physics" model of the toy in order to add physics mode
            guard let model = toyAnchor.children.first as? (Entity & HasPhysics)
            else { return }

            model.generateCollisionShapes(recursive: true)
            model.physicsBody = PhysicsBodyComponent(shapes: [.generateBox(size: .one)],
                                                       mass: 1.0,
                                                   material: .default,
                                                   mode: .kinematic)

            // Finally add the toy anchor to the scene
            self.arView.scene.addAnchor(toyAnchor)
            
        }
    
    }
    
    @objc public func didPan(_ sender: UIPanGestureRecognizer) {
        print(sender.state)
        let point = sender.location(in: self.arView)
        switch sender.state {
        case .began:
            if let entity = self.arView.hitTest(point).first?.entity {
                self.panGestureEntity = entity
            } else {
                self.panGestureEntity = nil
            }
        case .changed:
            if let entity = self.panGestureEntity {
                if let raycast = arView
                    .scene
                    .raycast(origin: .zero, direction: .one, length: 1, query: .all, mask: .all, relativeTo: entity)
                    .first
                {
                    print("hit", raycast.entity, raycast.distance)
                }

          }
        case .ended:
            self.panGestureEntity = nil
        default: break;
        }

    }
    
    
}
person Silvering    schedule 05.11.2020