Getting ObjImporter to parse quads

The App Hub’s Custom Model Importer sample loads 3D models of the ubiquitous .obj format, and is a straightforward way to get started with the XNA Content Processor. Not surprisingly, this sample just deals with handling the basics- it parses groups, converts triangles to vertices and loads diffuse textures, and not much else. It is not prepared to handle quad polygons, so I came up with a solution for it.

3D modeling with quads is very common and helpful for an artist, since they are better than triangles for reading the topology of the model, but they are not too friendly for real-time game rendering. Graphics hardware prefers triangles, and any quads from models imported into programs need to be handled appropriately. Either they may get totally ignored or crash your program when something goes wrong in your file importer. Using simpler file formats makes it a whole lot easier to understand how to solve such problems, and use the XNA content pipeline for your own needs.

The ObjImporter class of the custom model importer can be updated to handle quads in a certain winding order. I have modified it so that it can either read triangles or quads with vertices winding clockwise. To load models with quads, simply open ObjImporter.cs and look for the “f” case found in the ParseObjLine method (found around line 200) and replace it with the following code I wrote here:

// Faces (triangles and quads are supported)
case "f":

    // For triangles
    int[] polyIndices = {
        0, 1, 2
    };

    // Change the indices if a quad is needed
    if (lineTokens.Length > 4)
    {
        Array.Resize(ref polyIndices, 6);
        polyIndices[2] = 3;
        polyIndices[3] = 1;
        polyIndices[4] = 2;
        polyIndices[5] = 3;
    }

    // If the builder is null, this face is outside of a group
    // Start a new, unnamed group
    if (meshBuilder == null)
        StartMesh(null);

    // For each triangle vertex
    for (int vertexIndex = 0; vertexIndex < polyIndices.Length; vertexIndex++)
    {
        // Each vertex is a set of three indices:
        // position, texture coordinate, and normal
        // The indices are 1-based, separated by slashes
        // and only position is required.
        string[] indices = lineTokens[polyIndices[vertexIndex] + 1].Split('/');

        // Required: Position
        int positionIndex = int.Parse(indices[0],
            CultureInfo.InvariantCulture) - 1;

        if (indices.Length > 1)
        {
            // Optional: Texture coordinate
            int texCoordIndex;
            Vector2 texCoord;
            if (int.TryParse(indices[1], out texCoordIndex))
                texCoord = texCoords[texCoordIndex - 1];
            else
            texCoord = Vector2.Zero;

            // Set channel data for texture coordinate for the following
            // vertex. This must be done before calling AddTriangleVertex
            meshBuilder.SetVertexChannelData(textureCoordinateDataIndex,
                texCoord);
        }

        if (indices.Length > 2)
        {
            // Optional: Normal
            int normalIndex;
            Vector3 normal;
            if (int.TryParse(indices[2], out normalIndex))
                normal = normals[normalIndex - 1];
            else
                normal = Vector3.Zero;

            // Set channel data for normal for the following vertex.
            // This must be done before calling AddTriangleVertex
            meshBuilder.SetVertexChannelData(normalDataIndex, normal);
        }

        // Add the vertex with the vertex data that was just set
        meshBuilder.AddTriangleVertex(positionMap[positionIndex]);
    }
    break;

The notable differences here is the setup of polygon indices for both triangle and quad cases, and the vertexIndex loop parses index data from the order given directly by the index array and not straight as it is read from the line. I suppose there should still be a warning and a break to skip the addition of vertices if a n-gon has more than 4 sides, but these are rare.

Hope this helps people wanting to import mixed-polygon models into their programs!

Advertisements

2 thoughts on “Getting ObjImporter to parse quads

  1. If you use this code, you can then no longer load the Tank model that comes with the Custom model importer project, unless I really did miss a piece of your code?

    • Are you getting an error with loading the model at runtime, or getting the model to build in the content pipeline? As far as I can tell, I’ve modified the code in the same Model Importer project and I can still run it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s