Andy Jazz
Andy Jazz

Reputation: 58503

Can't feed a MDLMesh container with 3D model as SCNGeometry

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.

Upvotes: 5

Views: 2209

Answers (3)

Andy Jazz
Andy Jazz

Reputation: 58503

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

Upvotes: 1

Brandon
Brandon

Reputation: 323

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.

Upvotes: 2

EmilioPelaez
EmilioPelaez

Reputation: 19912

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.

Upvotes: 1

Related Questions