3
votes

I'm importing some simple OBJ assets using ModelIO like so:

let mdlAsset = MDLAsset(url: url, vertexDescriptor: nil, bufferAllocator: nil, preserveTopology: true, error: nil)

... and then adding them to a SceneKit SCN file. But, whenever I have meshes that have both quads/tris (often the case, for example eyeball meshes), the resulting mesh is jumbled:

Incorrect mesh topology

Re-topologizing isn't a good option since I sometimes have low-poly meshes with very specific topology, so I can't just set preserveTopology to false... I need a result with variable topology (i.e. MDLGeometryType.variableTopology).

How do I import these files correctly preserving their original topology?

1
The issue seems to be fixed in the latest iOS 11.3 betamnuages

1 Answers

4
votes

I reported this as a bug at Apple Bug Reporter on 25th of November, bug id: 35687088

Summary: SCNSceneSourceLoadingOptionPreserveOriginalTopology does not actually preserve the original topology. Instead, it converts the geometry to all quads, messing up the 3D model badly. Based on its name it should behave exactly like preserveTopology of Model IO asset loading.

Steps to Reproduce: Load an OBJ file that has both triangles and polygons using SCNSceneSourceLoadingOptionPreserveOriginalTopology and load the same file into an MDLMesh using preserveTopology of ModelIO. Notice how it only works properly for the latter. Even when you create a new SCNGeometry based on the MDLMesh, it will "quadify" the mesh again to contain only quads (while it should support 3-gons and up).

On December 13th I received a reply with a request for sample code and assets, which I supplied 2 days later. I have not received a reply since (hopefully because they are just busy from catching up from the holiday season...).

As I mentioned in my bug report's summary, loading the asset with Model I/O does work properly, but then when you create a SCNNode based on that MDLMesh it ends up messing up the geometry again.

In my case the OBJ files I load have a known format as they are always files also exported with my app (no normals, colors, UV). So what I do is load the information of the MDLMesh (buffers, facetopology etc) manually into arrays, from which I then create a SCNGeometry manually. I don't have a complete separate piece of code of that for you as it is a lot and mixed with a lot of code specific to my app, and it's in Objective C. But to illustrate:

    NSError *scnsrcError;
    MDLAsset *asset = [[MDLAsset alloc] initWithURL:objURL vertexDescriptor:nil bufferAllocator:nil preserveTopology:YES error:&scnsrcError];
    NSLog(@"%@", scnsrcError.localizedDescription);

    MDLMesh * newMesh = (MDLMesh *)[asset objectAtIndex:0];

    for (MDLSubmesh *faces in newMesh.submeshes) {

        //MDLSubmesh *faces = newMesh.submeshes.firstObject;
        MDLMeshBufferData *topo = faces.topology.faceTopology;
        MDLMeshBufferData *vertIx = faces.indexBuffer;
        MDLMeshBufferData *verts = newMesh.vertexBuffers.firstObject;

        int faceCount = (int)faces.topology.faceCount;

        int8_t *faceIndexValues = malloc(faceCount * sizeof(int8_t));
        memcpy(faceIndexValues, topo.data.bytes, faceCount * sizeof(int8_t));

        int32_t *vertIndexValues = malloc(faces.indexCount * sizeof(int32_t));
        memcpy(vertIndexValues, vertIx.data.bytes, faces.indexCount * sizeof(int32_t));

        SCNVector3 *vertValues = malloc(newMesh.vertexCount * sizeof(SCNVector3));
        memcpy(vertValues, verts.data.bytes, newMesh.vertexCount * sizeof(SCNVector3));

    ....
    ....
    }

In short, the preserveTopology option in SceneKit isn't working properly. To get from the working version in Model I/O to SceneKit I basically had to write my own converter.