Loading a Mesh From a File: Skull Demo
Model Data Format
Mr. Luna uses a very simple text format for the model in this example. It starts with a header that gives the number of vertices and the number of triangles. Next, it lists the vertex data (in position, normal pairs), and finally lists the triangle indices. Here is a short excerpt from the skull.txt model file (the full file is over 90,000 lines, I would suggest that you download it from http://www.d3dcoder.net/d3d11.htm or grab it from my GitHub repository at https://github.com/ericrrichards/dx11.git):
VertexCount: 31076 TriangleCount: 60339 VertexList (pos, normal) { 0.592978 1.92413 -2.62486 0.572276 0.816877 0.0721907 0.571224 1.94331 -2.66948 0.572276 0.816877 0.0721907 0.609047 1.90942 -2.58578 0.572276 0.816877 0.0721907 1.12127 1.64042 -1.94785 -0.0941668 0.904117 0.416779 1.04654 1.60198 -1.86107 0.0955379 0.877073 0.47076 1.06964 1.60894 -1.87873 0.0955378 0.877073 0.47076 1.151 1.65245 -1.7697 -0.378509 0.925514 -0.01241 1.11866 1.63277 -1.6602 -0.373131 0.92607 0.0562804 1.13374 1.64126 -1.6999 -0.373131 0.92607 0.0562804 ... } TriangleList { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ... }
Loading the Model Data
We will be loading the model data in our BuildGeometryBuffers() function. This is one of the rare times where it is actually more convenient to use C++, rather than C#; the C++ std::ifstream interface is a little bit more simple to use for reading this particular data than the .NET Stream/StreamReader options.
Note: There may be an easier way to do the file input of which I am not aware. If that’s the case, please let me know in the comments.
StreamReader doesn’t allow us to read in a token as easily as the C++ stream operators do, so we will need to read the file line-by-line, and parse out the data that we need. Fortunately, the file format is very simple, so that we need only use string.Split() to extract out the data on each line.
private void BuildGeometryBuffers() { try { var vertices = new List<Vertex>(); var indices = new List<int>(); var vcount = 0; var tcount = 0; using (var reader = new StreamReader("Models\\skull.txt")) { var input = reader.ReadLine(); if (input != null) // VertexCount: X vcount = Convert.ToInt32(input.Split(new[] {':'})[1].Trim()); input = reader.ReadLine(); if (input != null) //TriangleCount: X tcount = Convert.ToInt32(input.Split(new[] { ':' })[1].Trim()); var c = Color.Black; // skip ahead to the vertex data do { input = reader.ReadLine(); } while (input != null && !input.StartsWith("{")); // Get the vertices for (int i = 0; i < vcount; i++) { input = reader.ReadLine(); if (input != null) { var vals = input.Split(new[] {' '}); vertices.Add(new Vertex( new Vector3( Convert.ToSingle(vals[0].Trim()), Convert.ToSingle(vals[1].Trim()), Convert.ToSingle(vals[2].Trim())), c)); } } // skip ahead to the index data do { input = reader.ReadLine(); } while (input != null && !input.StartsWith("{")); // Get the indices _skullIndexCount = 3*tcount; for (var i = 0; i < tcount; i++) { input = reader.ReadLine(); if (input == null) { break; } var m = input.Trim().Split(new[] { ' ' }); indices.Add(Convert.ToInt32(m[0].Trim())); indices.Add(Convert.ToInt32(m[1].Trim())); indices.Add(Convert.ToInt32(m[2].Trim())); } } var vbd = new BufferDescription(Vertex.Stride*vcount, ResourceUsage.Immutable, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); _vb = new Buffer(Device, new DataStream(vertices.ToArray(), false, false), vbd); var ibd = new BufferDescription(sizeof (int)*_skullIndexCount, ResourceUsage.Immutable, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); _ib = new Buffer(Device, new DataStream(indices.ToArray(), false, false), ibd); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
Anything Else?
No, not really… You may note that we are drawing in wireframe mode again, as we did with the Shapes Demo; if we rendered the skull solid, without lighting, we would not be able to see much of the detail.
Next Time
We’ll be expanding on our Hills Demo from earlier, adding a dynamic mesh that will be updated each frame to represent the oceans.