gpt4 book ai didi

swift - ARKit - 如何使用 SceneKit 选择特定节点

转载 作者:行者123 更新时间:2023-11-30 10:30:59 25 4
gpt4 key购买 nike

我是 arkit 框架的新手,但有一个小问题。我有这个项目,我可以使用对象 .scn,将其放置在平面上,移动、旋转和缩放。我的问题是:如果我放置一个对象,我可以处理它,但是当我放置第二个对象时,不可能移动第一个对象。所以现在我只能在最后一个节点上做一些事情,但我想使用所有这些节点。

代码 View Controller


import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

//=================== VARIABLES ========================//

//is used to take memory about the selection that user do
var selectedNode: SCNNode?

//collection of node for deleting function
var placedNodes = [SCNNode]()
var planeNodes = [SCNNode]()

//Point where is the last node
var lastObjectPlacedPoint: CGPoint?

@IBOutlet var sceneView: ARSCNView!
let configuration = ARWorldTrackingConfiguration()

//selections for the segmented control
enum ObjectPlacementMode {
case noselection, plane
}

//this variable of ObjectPlacementMode type, represents the 3 selections in the segmented control. Default is .freeform
var objectMode: ObjectPlacementMode = .noselection{
didSet{
reloadConfiguration(removeAnchor: false)
}
}

//variable to hide the plane
var showPlanOverlay = false{
didSet{
for node in planeNodes{
node.isHidden = !showPlanOverlay
}
}
}

//================== VIEW LIFECYCLE ======================//

override func viewDidLoad() {
super.viewDidLoad()

sceneView.delegate = self
sceneView.autoenablesDefaultLighting = true

registerGestureRecognizers()

}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
reloadConfiguration()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}

// ================== PREPARE FOR SEGUE ===================//

//prepare for segue to pass the selected option to the optionController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showOptions" {
let optionsViewController = segue.destination as! OptionsContainerViewController
optionsViewController.delegate = self
}
}

// ================== IBACTION FUNCTION ===================//

//this update the variable whe you click
@IBAction func changeObjectMode(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
objectMode = .noselection
case 1:
objectMode = .plane
default:
break
}
}

// ================== TOUCH FUNCTION =====================//

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//recall parent implementation
super.touchesBegan(touches, with: event)

//to verify that the user has chosen an object from the popover i check if the selectedNode has a value and if there is at least one finger touch
guard let node = selectedNode,
let touch = touches.first else {return}

switch objectMode {

//if the user select freeform, i pass the node to the method
case .noselection:
// let touchPoint = touch.location(in: sceneView)
break
case .plane:
let touchPoint = touch.location(in: sceneView)
addNode(node, toPlaneUsingPoint: touchPoint)

}
}

//when the user lifts the finger from the screen
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)

lastObjectPlacedPoint = nil
}

// ===================== GESTURE RECOGNIZER WITH FUNCTION (MOVE, PINCH AND ROTATE) ======================= //

private func registerGestureRecognizers(){
let moveGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(move))
moveGestureRecognizer.minimumPressDuration = 0.1
self.sceneView.addGestureRecognizer(moveGestureRecognizer)

let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(scale))
self.sceneView.addGestureRecognizer(pinchGestureRecognizer)

let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(rotate))
self.sceneView.addGestureRecognizer(rotationGestureRecognizer)
}

@objc func move(recognizer: UILongPressGestureRecognizer){
guard let scene = recognizer.view as? ARSCNView else {return}
let touch = recognizer.location(in: scene)
let results = sceneView.hitTest(touch, types: [.existingPlaneUsingExtent])

if let match = results.first {
let t = match.worldTransform
placedNodes.last?.position = SCNVector3(x: t.columns.3.x, y: t.columns.3.y, z: t.columns.3.z)
}
}

@objc func scale(sender: UIPinchGestureRecognizer) {
let sceneView = sender.view as! ARSCNView
let pinchLocation = sender.location(in: sceneView)
let hitTest = sceneView.hitTest(pinchLocation)

if !hitTest.isEmpty {

let results = hitTest.first!
let node = results.node
let pinchAction = SCNAction.scale(by: sender.scale, duration: 0)
print(sender.scale)
// node.runAction(pinchAction)
placedNodes.last?.runAction(pinchAction)
sender.scale = 1.0
}

}

@objc func rotate(sender: UIRotationGestureRecognizer) {
let sceneView = sender.view as! ARSCNView
let holdLocation = sender.location(in: sceneView)
let hitTest = sceneView.hitTest(holdLocation)

if !hitTest.isEmpty {

let result = hitTest.first!

if sender.state == .began {
let rotation = SCNAction.rotateBy(x: 0, y: CGFloat(360.degreesToRadians), z: 0, duration: 1)
// let forever = SCNAction.repeatForever(rotation)
// result.node.runAction(rotation)
placedNodes.last?.runAction(rotation)
} else if sender.state == .ended {
result.node.removeAllActions()
}
}
}

// ==================== DETECT PLANE, CREATE FLOOR AND ADD NODE TO PLAN DETECTED ========================= //

//with this method you can recognize image or plane, in order to add a node on it
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {

if let planeAnchor = anchor as? ARPlaneAnchor{

nodeAdded(node, for: planeAnchor)

}
}

func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor,
let planeNode = node.childNodes.first,
let plane = planeNode.geometry as? SCNPlane else{ return }

//update the dimension of the plane
planeNode.position = SCNVector3(planeAnchor.center.x, 0, planeAnchor.center.z)
plane.width = CGFloat(planeAnchor.extent.x)
plane.height = CGFloat(planeAnchor.extent.z)
}

func createFloor(planeAnchor: ARPlaneAnchor) -> SCNNode {

let node = SCNNode()

//here i'm creating a plane with size that match the dimension of the planeAnchor
let geometry = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))

node.geometry = geometry

//Rotation of the plane 90 degree
node.eulerAngles.x = -Float.pi / 2
node.opacity = 0.25

return node
}

// ======================== ADD NODE FUNCTION ========================= //

//when you tap, will be created a clone node that will be added to the scene and in the list
func addNodeToSceneRoot(_ node:SCNNode){
let cloneNode = node.clone()
sceneView.scene.rootNode.addChildNode(cloneNode)
placedNodes.append(cloneNode)
}

//when you detect a plane, create a node. From now every other node will be children of this, not to the root node. Control that the object must be on a plane
func addNode(_ node:SCNNode, toPlaneUsingPoint point: CGPoint){

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

if let match = results.first {

let t = match.worldTransform
node.position = SCNVector3(x: t.columns.3.x, y: t.columns.3.y, z: t.columns.3.z)
addNodeToSceneRoot(node)

lastObjectPlacedPoint = point
}
}

func nodeAdded(_ node: SCNNode, for anchor: ARPlaneAnchor){
let floor = createFloor(planeAnchor: anchor)
floor.isHidden = !showPlanOverlay

//add node (plane) to the scene, and add it to the array
node.addChildNode(floor)
planeNodes.append(floor)
}

// ======================== RELOAD CONFIGURATION ============================ //

//this method reload the session based on the value of the objectMode, with plane detection always active
func reloadConfiguration(removeAnchor: Bool = true){

configuration.planeDetection = [.horizontal, .vertical]

let options: ARSession.RunOptions

if removeAnchor {
options = [.removeExistingAnchors]

for node in planeNodes{
node.removeFromParentNode()
}

planeNodes.removeAll()

for node in placedNodes{
node.removeFromParentNode()
}
placedNodes.removeAll()

}else{
options = []
}

sceneView.session.run(configuration, options: options)
}

}

// =========================== EXTENSION ============================= //

//this list of method will be called whe you select an option in the Options menu
extension ViewController: OptionsViewControllerDelegate {

//this one is called whe the user has selected the shape, color, size
func objectSelected(node: SCNNode) {
dismiss(animated: true, completion: nil)
selectedNode = node
}

//this is called when the user tap on Enable/Disable Plane Visualization
func togglePlaneVisualization() {
dismiss(animated: true, completion: nil)
showPlanOverlay = !showPlanOverlay
}

//this is called when the user tap on Undo Last Object, and locate the last node, remove it from the scene and remove it from the collection
func undoLastObject() {
if let lastNode = placedNodes.last{
lastNode.removeFromParentNode()
placedNodes.removeLast()
}
}

//this is called when the user tap on Reset Scene
func resetScene() {
dismiss(animated: true, completion: nil)
reloadConfiguration()
}
}

extension Int {

var degreesToRadians: Double { return Double(self) * .pi/180}
}


感谢大家对我的帮助。

最佳答案

您的手势函数正在修改 placedNodes.last 而不是 selectedNode

关于swift - ARKit - 如何使用 SceneKit 选择特定节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59239015/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com