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