5

I'm creating AR app (Xcode 10.1, Swift 4.2.1).

I'd like to load USDZ 3D object into an empty SceneKit's scene and then process it as MDL mesh.

Here's my code:

import ARKit
import SceneKit.ModelIO

let scene = SCNScene(named: "art.scnassets/emptyScene.scn")!

if let filePath = Bundle.main.path(forResource: "Helicopter", 
                                        ofType: "usdz", 
                                   inDirectory: "art.scnassets") {

    let refURL = URL(fileURLWithPath: filePath)
    let refNode = SCNReferenceNode(url: refURL)
    refNode?.load()
    scene.rootNode.addChildNode(refNode!)
}

let helicopterGeo = refNode!.geometry

let mdlMesh = MDLMesh(scnGeometry: helicopterGeo!)      // ERROR APPEARS HERE
try! mdlMesh.makeVerticesUniqueAndReturnError()
let flattenedGeometry = SCNGeometry(mdlMesh: mdlMesh)
let flattenedNode = SCNNode(geometry: flattenedGeometry)
scene.rootNode.addChildNode(flattenedNode)


But compiler gives me an error:

"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"

The question is: what approach should I use to assign a "Helicopter.usdz" geometry to a helicopterGeo constant?

Help me find a workaround, please!

You can download USDZ file for testing HERE.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220

3 Answers3

2

This should work:

var scene: SCNScene!
if let filePath = Bundle.main.path(forResource: "Helicopter", 
                                    ofType: "usdz", 
                               inDirectory: "art.scnassets") {

    let refURL = URL(fileURLWithPath: filePath)
    let mdlAsset = MDLAsset(url: refURL)
    scene = SCNScene(mdlAsset: mdlAsset)

}

SCNReferenceNode only works for .scn files. You can then get the geometry from a child node of the rootNode of the scene.

let helicopterNode = scene.rootNode.childNode(withName: "helicopter", recursively: true)
let geometry = helicopterNode.geometry!

Edit

Using one of the files from the AR Quick Look Gallery I managed to get this code to work. The main problem that I had was with the name of the specific child node, there was one called "RetroTV" but it did not have any geometry attached to it, it was just the parent for both "RetroTVBody" and "RetroTVScreen." The only problem is that it isn't loading the textures for the geometry.

var scene: SCNScene!
if let filePath = Bundle.main.path(forResource: "retrotv",
                                   ofType: "usdz",
                                   inDirectory: "art.scnassets") {

    let refURL = URL(fileURLWithPath: filePath)
    let mdlAsset = MDLAsset(url: refURL)
    scene = SCNScene(mdlAsset: mdlAsset)

    let tvNode = scene.rootNode.childNode(withName: "RetroTVBody", recursively: true)
    let geometry = tvNode!.geometry!

} else {

    print("invalid path!")

}

The above code also works with the tvNode and geometry declarations outside of the if let statement.

Brandon
  • 323
  • 2
  • 12
  • @argeo It is possible that `helicopterNode` is nil, do you know the actual name of the helicopter geometry in the udsz file? – Brandon Nov 15 '18 at 19:48
  • @argeo I don't have a usdz file to test with, but is it a run time or a compile time error, try force unwrapping `helicopterNode` and see if anything changes – Brandon Nov 15 '18 at 19:56
  • @argeo Is the code inside of your `if let` being called, I tested it and that is the problem I had. – Brandon Nov 15 '18 at 20:18
  • @ARGeo It seems that the issue is in the `withName` argument of `scene.rootNode.childNode` – Brandon Nov 15 '18 at 22:53
  • @ARGeo try printing out `scene.rootNode.childNodes` to see if it is actually working. – Brandon Nov 15 '18 at 23:17
  • Thanks for your answer, Brandon! Bounty will be awarded to you automatically. – Andy Jazz Nov 16 '18 at 12:27
1

I don't have an exact answer, but what I would do in your situation would be to examine the hierarchy of refNode.

Place a breakpoint after it's loaded and use the debugger to see if it's got any children. Do those children have any geometries? Do they have any children with geometry?

When creating 3D assets, sometimes multiple sections will be grouped on a parent node, and in many cases that parent node is empty.

EmilioPelaez
  • 18,758
  • 6
  • 46
  • 50
1

Was it an error in Maya binary file, or an error of usdz conversion – I don't know exactly. Xcode didn't see a correct name of the object in the simplest hierarchy of a Scene graph: Instead of pHelicopter1 it just showed Helicopter. My 3D object was made from pCube1 using polygonal Extrude tool.

Here's the final code and it works fine:

import ARKit
import SceneKit.ModelIO

//..........................................................

var scene = SCNScene(named: "art.scnassets/EmptyScene.scn")!

if let filePath = Bundle.main.path(forResource: "Helicopter",
                                        ofType: "usdz",
                                   inDirectory: "art.scnassets") {

    let refURL = URL(fileURLWithPath: filePath)
    let mdlAsset = MDLAsset(url: refURL)
    scene = SCNScene(mdlAsset: mdlAsset)
    let helicopterNode = scene.rootNode.childNode(withName: "pHelicopter1", 
                                               recursively: true)

    let geometry = helicopterNode!.geometry!
    let mdlMesh = MDLMesh(scnGeometry: geometry)
    try! mdlMesh.makeVerticesUniqueAndReturnError()
    let flattenedGeometry = SCNGeometry(mdlMesh: mdlMesh)
    let flattenedNode = SCNNode(geometry: flattenedGeometry)
    scene.rootNode.addChildNode(flattenedNode)

} else {
    print("Invalid path!")
}

enter image description here

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220