I have been able to successfully export the
ARMeshGeometry with a color texture as a
USDZ file (I've not succeeded in doing so as an
.obj; the
.obj ends up looking very peculiar and the colors do not appear as expected). My understanding of 3D modeling and working with 3D technologies is cursory at best, but I was pretty interested in understanding how to do this. There are a few things to consider, especially if your starting point is the
Visualizing and Interacting with a Reconstructed Scene sample project;
The sample project is showing the beautiful mesh with colors by leveraging the arView.debugOptions.insert(.showSceneUnderstanding) call. This shows the mesh, with colors, using a debug method, which is not publicly accessible, nor representative of any particular color scheme relevant for export.
As far as I can tell, the sample project leverages RealityKit for rendering AR content, which does not have a method to generate a mesh from the ARMeshGeometry and texture it, nor does it have a built-in way of exporting any sort of 3D model as something like SceneKit does.
My approach was to set up a new AR project leveraging the SceneKit content technology, rather than RealityKit. This then assumes that you;
Set up an ARWorldTrackingConfiguration with the configuration's sceneReconstruction property set to .meshWithClassification.
Set up your ARSCNViewDelegate to receive delegate calls related to your ARSCNView.
In my
ARSCNViewDelegate method, I have the following functions configured;
Code Block | func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { |
|
| guard let meshAnchor = anchor as? ARMeshAnchor else { |
| return nil |
| } |
|
| let geometry = SCNGeometry(arGeometry: meshAnchor.geometry) |
| |
| let classification = meshAnchor.geometry.classificationOf(faceWithIndex: 0) |
|
| let defaultMaterial = SCNMaterial() |
| defaultMaterial.fillMode = .lines |
| defaultMaterial.diffuse.contents = colorizer.assignColor(to: meshAnchor.identifier, classification: classification) |
| geometry.materials = [defaultMaterial] |
|
| let node = SCNNode() |
| node.geometry = geometry |
| return node |
| } |
Code Block | func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) { |
| guard let meshAnchor = anchor as? ARMeshAnchor else { |
| return |
| } |
|
| let newGeometry = SCNGeometry(arGeometry: meshAnchor.geometry) |
| |
| let classification = meshAnchor.geometry.classificationOf(faceWithIndex: 0) |
|
| let defaultMaterial = SCNMaterial() |
| defaultMaterial.fillMode = .lines |
| defaultMaterial.diffuse.contents = colorizer.assignColor(to: meshAnchor.identifier, classification: classification) |
| newGeometry.materials = [defaultMaterial] |
|
| node.geometry = newGeometry |
| } |
In general, I am using the
ARMeshGeometry to create a
SCNGeometry, classifying the "type" of mesh this is (table, floor, door, etc.) and setting a color based on that, then returning (or updating) the relevant node. Once you are ready to export your scene to a model, you can leverage SceneKit's
write(to:options:delegate:progressHandler:) method. For example, if your
ARSCNView is a property called
arView, you could call;
Code Block | let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) |
| let documentsDirectory = paths[0] |
| let filename = directory.appendingPathComponent("mesh.usdz") |
| self.arView.scene.write(to: filename, options: nil, delegate: nil, progressHandler: nil) |
The general idea is to recreate the
ARMeshGeometry using a
SCNGeometry, set texture colors using whatever methodology works for your needs, then save the scene. Per the above code samples, there are some extensions necessary to convert the
ARMeshGeometry to a
SCNGeometry, which I will share in a follow-up post, as I believe there is a length limit here.