Home > Software design >  Node name was overwritten by USDZ model
Node name was overwritten by USDZ model

Time:06-18

I'm currently developing an iOS app that uses ARKit and SceneKit for the augmented reality. I'm having a problem while loading an .usdz model into the scene. I can load it correctly into the scene, but, when I try to get the node name (in which I loaded the .usdz model) after tapping on it, it returns the .usdz name and not the name I gave to it.

The code I use to load the .usdz model is:

let mdlAsset = MDLAsset(url: urlPath)
mdlAsset.loadTextures()
let asset = mdlAsset.object(at: 0) // extract first object
var assetNode = SCNNode(mdlObject: asset)
assetNode = SCNNode(mdlObject: asset)
assetNode.name = "Node-2"
sceneView.scene.rootNode.addChildNode(assetNode)

To capture the tap on the node, the code is:

@objc func handleTap(recognizer: UITapGestureRecognizer){
       
    let location = recognizer.location(in: sceneView)
    let results = sceneView.hitTest(location, options: nil)

    guard recognizer.state == .ended else { return }

    if results.count > 0 {
        let result = results[0] as SCNHitTestResult
        let node = result.node  
        print(node.name)    
    }     
}

As I mentioned before, when I tap on the object it prints Optional("Sphere_0") value that can be found in the top right corner, in the model details page. The correct value that I expected was "Node-2".

CodePudding user response:

The name of your USDZ model isn't overridden. It's the peculiarities of SceneKit hit-testing and scene hierarchy. When you perform a hit-test search, SceneKit looks for SCNGeometry objects (not a main node) along the ray you specify. So, all you need to do, once the hit-test is completed, is to find the corresponding parent nodes.

Try this code:

import SceneKit.ModelIO

class GameViewController: UIViewController {
    
    var sceneView: SCNView!
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        sceneView = (self.view as! SCNView)
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        sceneView.scene = SCNScene()
        sceneView.backgroundColor = .black
        
        let recog = UITapGestureRecognizer(target: self, action: #selector(tap))
        sceneView.addGestureRecognizer(recog)
        
        // ASSET
        let mdlAsset = MDLAsset(scnScene: scene)
        let asset = mdlAsset.object(at: 0)
        let node = SCNNode(mdlObject: asset.children[0])
        node.name = "Main-Node-Name"                       // former "ship"
        node.childNodes[0].name = "SubNode-Name"           // former "shipMesh"
        node.childNodes[0].childNodes[0].name = "Geo-Name" // former "Scrap_MeshShape"
        sceneView.scene?.rootNode.addChildNode(node)
    }
}

And your hit-testing method:

extension GameViewController {
    
    @objc func tap(recognizer: UITapGestureRecognizer) {
           
        let location = recognizer.location(in: sceneView)
        let results = sceneView.hitTest(location)

        guard recognizer.state == .ended else { return }

        if results.count > 0 {
            let result = results[0] as SCNHitTestResult
            let node = result.node
            
            print(node.name!)                            // Geo-Name
            print(node.parent!.name!)                    // SubNode-Name
            print(node.parent!.parent!.name!)            // Main-Node-Name
        }
    }
}
  • Related