I am trying to built an app where you can place dices at the table, that spawns automatically if the app detects horizontal plane. I will explain the problem after the code:
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
var isTableAdded = false
var diceArray = [SCNNode]()
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
sceneView.autoenablesDefaultLighting = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
// MARK: - ADDS DICE
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Tapped")
if let touch = touches.first {
let touchLocation = touch.location(in: sceneView)
let results = sceneView.hitTest(touchLocation, types: .existingPlaneUsingExtent)
if let hitResults = results.first {
// Create a new scene
let diceScene = SCNScene(named: "art.scnassets/diceCollada.scn")!
if let diceNode = diceScene.rootNode.childNode(withName: "Dice", recursively: true) {
diceNode.position = SCNVector3(
x: hitResults.worldTransform.columns.3.x,
y: hitResults.worldTransform.columns.3.y,
z: hitResults.worldTransform.columns.3.z)
diceArray.append(diceNode)
sceneView.scene.rootNode.addChildNode(diceNode)
}
}
}
}
// MARK: - RENDERS TABLE AT THE HORIZONTAL PLANE
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
print("plane detected")
if isTableAdded == false {
let planeAnchor = anchor
// let planeNode = SCNNode()
let x = planeAnchor.transform.columns.3.x
let y = planeAnchor.transform.columns.3.y
let z = planeAnchor.transform.columns.3.z
//planeNode!.position = SCNVector3(x: x, y: y, z: z)
let idleScene = SCNScene(named: "art.scnassets/Table.scn")!
let node = SCNNode()
for child in idleScene.rootNode.childNodes{
node.addChildNode(child)
}
node.position = SCNVector3(x,y,z)
node.scale = SCNVector3(0.001,0.001,0.001)
sceneView.scene.rootNode.addChildNode(node)
isTableAdded = true
}
print("Model placed")
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
print("plane updated")
}
}
The problem is here. (Video at Imgur). As you can see table is placed, but I don't quite understand how to place SCNScene directly at the other one. The only thing I have achieved is creating a model at the tap and it's placed only at the horizontal plane.
How can I spawn model on the other model?
You could probably use this tiny little
extension
to achive what you want. It basically is a helper function that allows you to grab all the geometry objects from any scene-file (.scn) and you can place them wherever you want. I use this function to compose/build one single scene (the default) from out of plenty of other scene-files, which hold individual geometries. It will return a SCNNode instead of a new SCNScene. Let me know, if you need more information, or if I missunderstand you.(place this code-snippet somewhere outside your
ViewController
)Then you can use it like this:
The
flattenedClone
is useful if you want to merge all geometries within a signleSCNNode
to one object. If you don't useflattenedClone
, you will find your geometries as individualchildNodes
of theobjectNode
.Keep in mind to add a static physicsBody to the Table (and probably the floor) and dynamic physicsBodies to your dices, if you want to keep them on the top of the table and prevent them to fall trough the ground (floor).