Load .obj to .scn with multiple sub-objects, textures, materials in SceneKit & Model I/O?

2.1k views Asked by At

I'm currently working with large .obj files in Apple's SceneKit/Model I/O that contain multiple objects within, each with separate textures and materials. This means I cannot apply one single texture to the file like many other form posts suggest. Is there a good way to import in the the materials and textures?

I have my obj mtl and jpg's all in one directory where I'm also putting the scn scene.

The code currently follows this design, where I access it from its respective location, load it into a MDLAsset then place it into a SCNScene where it is saved back to a file to be loaded in later in the code.

//...  

// Get the mesh from the obj object  
let asset = MDLAsset(url:url)  
//asset.loadTextures()  
guard let object = asset.object(at: 0) as? MDLMesh else {  
    fatalError("Failed to get mesh from obj asset.")  
}  

// Wrap the ModelIO object in a SceneKit object  
let scene = SCNScene()  
let node = SCNNode(mdlObject: object)  
scene.rootNode.addChildNode(node)  

// Get the document directory name the write a url for the object replacing its extention with scn  
let dirPaths = FileManager().urls(for: .documentDirectory, in: .userDomainMask)  
let fileUrl = dirPaths[0].appendingPathComponent(url.lastPathComponent.replacingOccurrences(of: ".obj", with: ".scn"))  

// Write the scene to the new url  
if !scene.write(to: fileUrl, delegate: nil) {  
    print("Failed to write scn scene to file!")  
    return nil  
}  

// ... 

The MDLAsset.loadTextures function has no documentation and only causes a memory leak so at the time of this post, it's not an option. Opening the model by hand and hitting the convert to SCNScene dosn't work either as I still lose the materials. Additionally I want this to be automated in code to allow for downloading and conversion of models at runtime.

It seems like there is not built in way to do this except to do each texture and material by hand in the code, which is easy when it's only one complete texture but this model might have 100 different materials. It looks like it requires me to parse the obj/mtl manually and then create and assign the materials by hand. This seems completely unreasonable and I figure there must be a better way that I don't know about.

1

There are 1 answers

1
Nick Porcino On

When you import an OBJ file via Model I/O as an MDLAsset, it will arrive as a collection of one or more MDLMeshes. The meshes will have MDLMaterials associated with them, and the MDLMaterial will have attributes. Those attributes will be numeric, file paths, or images. You need to iterate the properties, and check if there is a path.

https://developer.apple.com/documentation/modelio/mdlmaterialproperty

If there is, it will likely be a fileURL with the same content as was in the OBJ file's associated MTL file.

The properties described in the MDLScatteringFunction correspond to the various properties in a typical MTL file.

https://developer.apple.com/documentation/modelio/mdlscatteringfunction

MDLAsset.loadTextures will add an MDLTextureSampler value to the property, if Model IO can actually find the texture referenced in the MTL file.