如何固定 3D usdz 对象在屏幕中央的位置?

问题描述 投票:0回答:1

在我的应用程序中,我在 AR 世界中添加了一个 USDZ 对象。我想让物体跟随相机的移动并始终停留在屏幕中央。我做不到。

我尝试添加对象并更新它在渲染器中的位置确实有更新功能,但它没有用。这是我的代码:

import UIKit
import ARKit
import SceneKit
import simd

class ARViewController: UIViewController, UIGestureRecognizerDelegate {

    @IBOutlet weak var arView: ARSCNView!
    var sceneView: ARSCNView!
    var cameraNode: SCNNode!
    let position = SCNVector3(x: 0, y: 5, z: 10)
    var scnView: SCNView!
    var scnScene: SCNScene!
    var popupView = CustomViewAR()
    var latitude = CLLocationDegrees()
    var longitude = CLLocationDegrees()
    var isShow : Bool?
    let popupPlane = SCNPlane()
    var popupNode = SCNNode()
    var tapgest = UITapGestureRecognizer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        config.environmentTexturing = .automatic
        arView.delegate = self
        arView.session.run(config)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            self.addObject()
        }
        addTapGesture()
        setUpPopUpView()

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
      let configuration = ARWorldTrackingConfiguration()
      arView.session.run(configuration)
    }
      
    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      arView.session.pause()
    }
    func addObject(){
        
        
        let scene = SCNScene()
        let usdzNode = SCNNode()
//
        let usdzScene = SCNScene(named: "cube.usdz")!
        let usdzChildNodes = usdzScene.rootNode.childNodes
        for node in usdzChildNodes {
            usdzNode.addChildNode(node)
        }

        usdzNode.position = SCNVector3(0, 0, -2) // set the position of the object

        scene.rootNode.addChildNode(usdzNode)
//        scene!.rootNode.position = SCNVector3(0, 0, -1)
        let ambientLightNode = SCNNode()
                       ambientLightNode.light = SCNLight()
                       ambientLightNode.light?.type = .ambient
                       ambientLightNode.light?.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
//        scene.rootNode.position = SCNVector3(0, 0, -1)
                        let cameraNode = SCNNode()
                        cameraNode.camera = SCNCamera()
                        // 3: Place camera
                        cameraNode.position = SCNVector3(x: 0, y: 0, z: -2)
                        // 4: Set camera on scene
        scene.rootNode.addChildNode(cameraNode)
//        arView.allowsCameraControl = true
        let simdVector = SIMD3<Float>(x: 100, y: 100, z: 100)
        let vector = SCNVector3(simdVector)
        scene.rootNode.scale = vector
        arView.scene = scene
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        arView.addGestureRecognizer(panGesture)

        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchGesture(_:)))
        arView.addGestureRecognizer(pinchGesture)
        
        let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotateGesture(_:)))
        arView.addGestureRecognizer(rotateGesture)

    }
    @objc func handleRotateGesture(_ gesture: UIRotationGestureRecognizer) {
        guard let currentFrame = arView.session.currentFrame else {
            return
        }
        
        let rotation = Float(gesture.rotation)
        let rotationDelta = simd_quatf(angle: rotation, axis: simd_float3(1, 1, 1))
        
        let usdzNode = arView.scene.rootNode.childNodes.first!
        let usdzTransform = usdzNode.simdTransform
        
        let newTransform = simd_mul(usdzTransform, simd_float4x4(rotationDelta))
        usdzNode.simdTransform = newTransform
        
        gesture.rotation = 0
    }

    @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
        guard let currentFrame = arView.session.currentFrame else {
            return
        }
        
        let translation = gesture.translation(in: arView)
        let deltaX = Float(translation.x) / Float(arView.bounds.width)
        let deltaY = Float(translation.y) / Float(arView.bounds.height)
        
        let usdzNode = arView.scene.rootNode.childNodes.first!
        var usdzTransform = usdzNode.simdTransform
        
        // Get the camera's orientation in world space
        let camera = currentFrame.camera
        let cameraTransform = camera.transform.inverse
//        let cameraForward = cameraTransform.forward
        let rotationFactor: Float = 10.0
        // Rotate the USDZ object around the world Y and X axes based on gesture translation
        usdzTransform *= simd_float4x4(simd_quatf(angle: -deltaX * rotationFactor, axis: simd_float3(0, 1, 0)))
        usdzTransform *= simd_float4x4(simd_quatf(angle: deltaY * rotationFactor, axis: simd_float3(1, 0, 0)))
        
        usdzNode.simdTransform = usdzTransform
        
        gesture.setTranslation(CGPoint.zero, in: arView)
    }




    @objc func handlePinchGesture(_ gesture: UIPinchGestureRecognizer) {
        guard let currentFrame = arView.session.currentFrame else {
            return
        }
        
        let usdzNode = arView.scene.rootNode.childNodes.first!
        let usdzTransform = usdzNode.simdTransform
        
        var scaleMatrix = matrix_identity_float4x4
        let scale = Float(gesture.scale)
        scaleMatrix.columns.0.x = scale
        scaleMatrix.columns.1.y = scale
        scaleMatrix.columns.2.z = scale
        
        let newTransform = simd_mul(usdzTransform, scaleMatrix)
        
        usdzNode.simdTransform = newTransform
        
        gesture.scale = 1
    }


    func addTapGesture()
    {
        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        arView.addGestureRecognizer(tap)
    }
    
    @objc func handleTap(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: arView)
        let hitResults = arView.hitTest(location, options: nil)
        
        if let hitNode = hitResults.first?.node {
            showPopup(nextTo: hitNode)
            
            tapgest = UITapGestureRecognizer(target: self, action: #selector(closePopup))
            arView.addGestureRecognizer(tapgest)

            isShow = true
        }
        
    }
    
    @objc func dismissPopupView() {
//        popupView.alpha = 0
    }
    
    func setUpPopUpView(){
        
        popupView = CustomViewAR().loadNib() as! CustomViewAR

        let geocoder = CLGeocoder()
        let location = CLLocation(latitude: latitude, longitude: longitude)

        geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
            guard error == nil else {
                print("Error: \(error!)")
                return
            }

            guard let placemark = placemarks!.first else {
                print("Error: placemark is nil")
                return
            }
            let address = "\(placemark.name ?? "Could not find info"), \( placemark.locality ?? "No City"), \( placemark.administrativeArea ?? "No Administrative Area"), \( placemark.country ?? "No Country")"
            self.popupView.setValues(labelName: placemark.name!, placeAddress: address)

        }
    }
    
    func showPopup(nextTo node: SCNNode) {
            
        popupView.isHidden = false
        setUpPopUpView()
        popupView.bringSubviewToFront(popupView.lblAddress)
        popupView.bringSubviewToFront(popupView.lblPlaceName)
        popupView.lblAddress.textColor = UIColor.black
            
        let worldPosition = node.simdWorldPosition - 30
        let x = worldPosition.x + 30 // adjust as needed
        let y = worldPosition.y + 30// adjust as needed
        
        popupView.frame = CGRect(x: Int(x), y: Int(y), width: 250, height: 250)
        
        popupPlane.width = 60 // adjust as needed
        popupPlane.height = 60 // adjust as needed
        popupPlane.cornerRadius = 10
        popupPlane.firstMaterial?.diffuse.contents = popupView
        
        popupNode = SCNNode(geometry: popupPlane)
//        popupNode.position = SCNVector3(0, 0, -1000)
        popupNode.opacity = 0.8
        popupNode.simdWorldPosition = worldPosition // set position using simdWorldPosition
        let constraint = SCNBillboardConstraint()
        constraint.freeAxes = [.Y]
        popupNode.constraints = [constraint]
            
        arView.scene.rootNode.addChildNode(popupNode)

        // Animate the popup to appear
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 0.5
        SCNTransaction.commit()
    }


    
    @objc func closePopup() {
        
        if isShow == true {
            SCNTransaction.begin()
                    SCNTransaction.animationDuration = 0.5
                    popupView.alpha = 0
                    SCNTransaction.commit()
            popupView.isHidden = true

            let configuration = ARWorldTrackingConfiguration()
            arView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
            arView.setNeedsDisplay()
            popupNode.isHidden = true
            isShow = false
            arView.removeGestureRecognizer(tapgest)
            setUpPopUpView()

        } else {
            
        }
    }

    func addAnimation(node: SCNNode) {
        let rotateOne = SCNAction.rotateBy(x: 0, y: CGFloat(Float.pi), z: 0, duration: 5.0)
        let hoverUp = SCNAction.moveBy(x: 0, y: 0.2, z: 0, duration: 2.5)
        let hoverDown = SCNAction.moveBy(x: 0, y: -0.2, z: 0, duration: 2.5)
        let hoverSequence = SCNAction.sequence([hoverUp, hoverDown])
        let rotateAndHover = SCNAction.group([rotateOne, hoverSequence])
        let repeatForever = SCNAction.repeatForever(rotateAndHover)
        node.runAction(repeatForever)
      }
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if touch.view == popupView || touch.view?.isDescendant(of: popupView) == true {
            
            return false
            
        }else{
            popupView.alpha = 0
            return true
        }
    }
    
    @IBAction func backBtnTapped(_ sender: Any) {
        self.dismiss(animated: true, completion: nil)
    }
}
extension ARViewController: ARSCNViewDelegate, SCNSceneRendererDelegate {
  func session(_ session: ARSession,
               didFailWithError error: Error) {
  print("Session Failed - probably due to lack of camera access")
}
  
func sessionWasInterrupted(_ session: ARSession) {
  print("Session interrupted")
}
  
func sessionInterruptionEnded(_ session: ARSession) {
  print("Session resumed")
 }
    
}
extension UIView {
    /** Loads instance from nib with the same name. */
    func loadNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nibName = "CustomViewAR"
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as! UIView
    }
}

ios swift scenekit arkit
1个回答
0
投票

您的

SCNView
ARSCNView
(取决于您使用哪种相机)都应该有一个所谓的
pointOfView
,这实际上是一个
SCNNode
。您可以尝试将您的对象(来自 USDZ 文件)添加到此节点,位置值为 i.Ex:
SCNVector3(0.0, 0.0, -2.0)
- 当然取决于它的大小。 (而不是将其添加到主场景的
rootNode
)。这应该使对象始终位于相机前面,无论您将相机或设备指向哪个方向(如果您使用 AR)。

© www.soinside.com 2019 - 2024. All rights reserved.