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!