Microsoft XNA Framework; Drawing a complex mesh

Now that we know how to draw primitives in XNA, it time to create something more complex then the simple triangle we created earlier. Lets look at the various tasks we need to complete to create a complex mesh.

  • Create a game component that will contain all of our logic
  • Initialize the mesh properties, preferably through the constructor
  • Create vertexdeclarations, buffers, matrices, textures, effects
  • Initialize a vertex buffer for the mesh
  • Initialize an index buffer for the mesh
  • Initialize an effect
  • Create an update method with game logic
  • Create a draw method

Although this looks like quite a list of stuff we want to implement, it actually takes a small effort the accomplish this. Before we continue, lets look at some of the keywords we haven't seen before.

Mesh
A mesh is an object that holds data. A mesh contains meshparts, which contain lists of vertices, indices, worldmatrices and effects. Rendering a mesh causes the object it represents (usually a 3d model) to be displayed on the screen. The mesh object contains logic to draw the meshparts on the screen using the correct World, View and Projection matrices and effects.

Vertexdeclaration
We already saw a vertexdeclaration in the previous article, but I didn't properly explain its value. A vertexdeclaration is a Microsoft.XNA.Graphics type. You need to set a vertexdeclaration on the graphicsdevice so you can start drawing primitives. The vertexdeclaration contains an array of vertexelements which specify the type of data in the vertexbuffer. The next article will feature a custom vertex element structure.

VertexBuffer
A vertexbuffer is an object that holds the vertex data. When setting data into a vertexbuffer, the data of the vertices is places sequentially in the data stream. This is why a VertexDeclaration is important, because it specifies how the data is stored so the GPU can properly read it.

IndexBuffer
We haven't seen an IndexBuffer before, but I'm going to use one in this article. An IndexBuffer is an array of shorts. Every short represents a vertex, where the number is the index of the vertex in the VertexBuffer. Lets say we want to draw a quad. The quad needs exactly four vertices, but can only be drawn with two triangles, and a triangle needs three vertices. When using IndexBuffers, we only need to create the four vertices, and an index for each time we want to use a vertex in a triangle. That adds up to four vertices, and six indices, instead of six vertices.

Effect
We've seen effects before, but what are the actually? Well, an effect is an object that is used to draw primitives. The effect holds data such as the world, view and projection matrices, but also custom parameters such as lightning or transformations. We can use an effect from a shader or the BasicEffect class that the XNA framework provides.

With that said, I've decided to create a custom mesh called torus. A torus is a doughnut shaped object, with a hole in the center. Lets start with a simple DrawableGameComponent that has methods to incorporate the various tasks we defined earlier:

C#:
  1. public class DrawTorus : DrawableGameComponent
  2. {
  3.     public DrawTorus(Game game)
  4.         : base(game)
  5.     {
  6.     }
  7.  
  8.     public override void Initialize()
  9.     {
  10.     }
  11.  
  12.     protected void InitializeVertexBuffer()
  13.     {
  14.     }
  15.     protected void InitializeEffect()
  16.     {
  17.     }
  18.  
  19.     protected override void LoadGraphicsContent(bool loadAllContent)
  20.     {
  21.  
  22.         base.LoadGraphicsContent(loadAllContent);
  23.     }
  24.  
  25.     protected override void UnloadGraphicsContent(bool unloadAllContent)
  26.     {
  27.  
  28.         base.UnloadGraphicsContent(unloadAllContent);
  29.     }
  30.  
  31.     public override void Update(GameTime gameTime)
  32.     {
  33.     }
  34.  
  35.     public override void Draw(GameTime gameTime)
  36.     {
  37.     }
  38. }

The torus has a few visual properties we want to define. These include the radius of the entire object (SegmentRadius), the radius/thickness of the torus (TubeRadius), and ofcourse the number of elements (Segments) totally and per tube (Tubes). I've defined a couple of properties in the DrawTorus class to access these values easily, aswell as an overloaded constructor to initialize them.

C#:
  1. protected int segmentRadius;
  2. protected int tubeRadius;
  3. protected int numSegments;
  4. protected int numTubes;
  5.  
  6. public int SegmentRadius
  7. {
  8.     get { return segmentRadius; }
  9.     set { segmentRadius = value; }
  10. }
  11. public int TubeRadius
  12. {
  13.     get { return tubeRadius; }
  14.     set { tubeRadius = value; }
  15. }
  16. public int Segments
  17. {
  18.     get { return numSegments; }
  19.     set { numSegments = value; }
  20. }
  21. public int Tubes
  22. {
  23.     get { return numTubes; }
  24.     set { numTubes = value; }
  25. }

C#:
  1. public DrawTorus(Game game, int segmentRadius, int tubeRadius, int segments, int tubes)
  2.     : base(game)
  3. {
  4.     this.SegmentRadius = segmentRadius;
  5.     this.TubeRadius = tubeRadius;
  6.     this.Segments = segments;
  7.     this.Tubes = tubes;
  8. }

Note that I haven't created a method that initializes the IndexBuffer. The IndexBuffer depends heavily on the VertexBuffer, and since it isn't possible to extract the data from the buffer, but only its source, it is more efficient to implement the initialization of the IndexBuffer in the InitializeVertices method.

Before we can continue, we need to calculate some variables to use for drawing. Although the torus is a complex mesh, the algorithm supporting this object is quite easy. We've already defined some constants that we need to set first, I've chosen to create five segments, and five tubes.

C#:
  1. DrawTorus drawTorus = new DrawTorus(this, 1000, 500, 5, 5);
  2. this.Components.Add(drawTorus);

When drawing a torus that has five segments and tubes, what kind of algorithm would we use? Well, to discover this, we can simply define the types of data we need. These include the number of vertices, the number of primitives and the number of indices.

The number of vertices of a torus is quite obvious, we multiply the number of segments with the number of tubes. Our torus has five positions around the center (segments), where each segment has five positions in depth (tubes). The number of primitives equals the number of vertices times two. This is because every vertex has one quad, and every quad has one vertex. Every quad has one vertex? What? Well it doesn't, but the other vertices in a quad are always shared with another quad. The reason we multiply this value with two is because Direct3D can't render quads, but triangles. And two triangles make up a single quad. The number of indices equals the number of primitives (triangles) times 3. This is because a triangle is made from three vertices.

I've used the DrawTorus.Initialize method to calculate these values.

C#:
  1. public override void Initialize()
  2. {
  3.     // Total vertices
  4.     this.totalVertices = this.Segments * this.Tubes;
  5.  
  6.     // Total primitives
  7.     this.totalPrimitives = this.totalVertices * 2;
  8.  
  9.     // Total indices
  10.     this.totalIndices = this.totalPrimitives * 3;
  11.  
  12.     base.Initialize();
  13. }

All we need to do now is create a point to call the mesh initialization code, a draw method, and we're set! As usual, I've overridden the Load- and UnloadGraphicsContent methods to load the content, so that seems like a logical place to call the DrawTorus.InitializeVertices method.

I'm also creating the VertexDeclaration in the LoadGraphicsContent method because we need to have a valid handle to the GraphicsDevice. As you can see, I've passed a VertexPositionColor element structure to the VertexDeclaration . There are many different vertex element structures, but this one is sufficient for our example at this time. The VertexPositionColor structure allows us to save a position and a color for each vertex. If we would want to apply a texture to the torus, we would need to use a structure that contains a VertexElement with a Vector2 format, but I'll save this for a future article. An example of a structure that has a texture element is the VertexPositionNormalTexture.

C#:
  1. protected override void LoadGraphicsContent(bool loadAllContent)
  2. {
  3.     // Create declaration
  4.     this.vertexPositionColorDeclaration = new VertexDeclaration(this.GraphicsDevice, VertexPositionColor.VertexElements);
  5.  
  6.     // Init world matrix
  7.     this.matrixWorld = Matrix.Identity;
  8.  
  9.     // Init the vertex buffer and effect
  10.     if (loadAllContent)
  11.     {
  12.         this.InitializeVertexBuffer();
  13.         this.InitializeEffect();
  14.     }
  15.  
  16.     base.LoadGraphicsContent(loadAllContent);
  17. }

Note that I haven't set the VertexDeclaration on the GraphicsDevice yet. This is something that typically has to be set before drawing the primitives, because other DrawableGameComponents can override this property with a another VertexDeclaration if that component needs it.

If the LoadGraphicsContent method initializes the VertexBuffer, IndexBuffer and the Effect, the UnloadGraphicsContent method should properly dispose these members. Just for the sake of clarity, here's the code I've used to do this:

C#:
  1. protected override void UnloadGraphicsContent(bool unloadAllContent)
  2. {
  3.     // Check to unload all the content
  4.     if (unloadAllContent)
  5.     {
  6.         // Displose the vertex and index buffers
  7.         this.vertexBuffer.Dispose();
  8.         this.indexBuffer.Dispose();
  9.  
  10.         // Dispose the effect
  11.         this.basicEffect.Dispose();
  12.     }
  13.  
  14.     base.UnloadGraphicsContent(unloadAllContent);
  15. }

Although the next logical step would be to create an implementation for the InitializeVertices method, it is easier to create the Draw method so we can see the process of the InitializeVertices and check how the DrawableGameComponent works this far. As you can see in the following code, it first sets the VertexDeclaration on the GraphicsDevice followed by the World, View and Projection matrices on the Effect. Then I Begin the effect and set the vertices and indices on the GraphicsDevice for each pass. Last but not least, I draw the primitives using the DrawIndexedPrimitives method, that method uses the indices set on the Graphics, which we obviously need.

C#:
  1. public override void Draw(GameTime gameTime)
  2. {
  3.     // Retrieve camera
  4.     ICameraManagerService cameraManagerService = (ICameraManagerService)this.Game.Services.GetService(typeof(ICameraManagerService));
  5.  
  6.     // Set the vertex declaration on the graphics device
  7.     this.GraphicsDevice.VertexDeclaration = this.VertexPositionColorDeclaration;
  8.  
  9.     // Enable depthbuffer, and disable alphablending
  10.     this.GraphicsDevice.RenderState.DepthBufferEnable = true;
  11.     this.GraphicsDevice.RenderState.AlphaBlendEnable = false;
  12.  
  13.     // Reverse the cullmode
  14.     this.GraphicsDevice.RenderState.PointSize = 5;
  15.  
  16.     // Set the World, View and Projection matrices
  17.     this.basicEffect.World = this.matrixWorld;
  18.     this.basicEffect.View = cameraManagerService.Camera.View;
  19.     this.basicEffect.Projection = cameraManagerService.Camera.Projection;
  20.  
  21.     // Begin effects
  22.     this.basicEffect.Begin();
  23.     {
  24.         // Loop through each pass
  25.         foreach (EffectPass pass in this.basicEffect.CurrentTechnique.Passes)
  26.         {
  27.             pass.Begin();
  28.  
  29.             // Set the vertices and indices
  30.             this.GraphicsDevice.Vertices[0].SetSource(this.vertexBuffer, 0, VertexPositionColor.SizeInBytes);
  31.             this.GraphicsDevice.Indices = this.indexBuffer;
  32.  
  33.             // Draw the model
  34.             this.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.PointList, 0, 0, this.totalVertices, 0, this.totalVertices);
  35.  
  36.             pass.End();
  37.         }
  38.     }
  39.  
  40.     // End effects
  41.     this.basicEffect.End();
  42.  
  43.     base.Draw(gameTime);
  44. }

Oops! Almost forgot to implement the initialization of the effect. Lets do this now.

C#:
  1. protected void InitializeEffect()
  2. {
  3.     this.basicEffect = new BasicEffect(this.GraphicsDevice, null);
  4.     this.basicEffect.VertexColorEnabled = true;
  5. }

Yes, the effect is a simple BasicEffect instance with the EnabledVertexColors property set to true. This property tells Direct3D to use the colors of the vertices. We need to do this because not all vertex elements have a color property and even if they do, we might not want to display them in some scenarios.

Great, so we're set! The first priority that is remaining is aligning the vertices correctly. But how are we going to do that exactly? What kind of data do we need? Well, all we really need is a filled VertexBuffer and IndexBuffer. So lets start by adding a couple of vertices to the VertexBuffer. The following implementation draws four vertices. And, because I didn't want to think too long about what to do with the indices, I've filled the indicesList with indices corresponding to the vertices.

C#:
  1. protected void InitializeVertexBuffer()
  2. {
  3.     // Init vertexList and indexList
  4.     List verticesList = new List();
  5.     List indicesList = new List();
  6.  
  7.     // Create 3 static vertices
  8.     verticesList.Add(new VertexPositionColor(new Vector3(-1.0f, 1.0f, 0.0f), Color.White));
  9.     verticesList.Add(new VertexPositionColor(new Vector3(1.0f, 1.0f, 0.0f), Color.White));
  10.     verticesList.Add(new VertexPositionColor(new Vector3(1.0f, -1.0f, 0.0f), Color.White));
  11.     verticesList.Add(new VertexPositionColor(new Vector3(-1.0f, -1.0f, 0.0f), Color.White));
  12.  
  13.     // Override the numVertices
  14.     this.totalVertices = verticesList.Count;
  15.  
  16.     // Fill the indices for the static vertices
  17.     for (short i = 0; i <verticesList.Count; i++)
  18.         indicesList.Add(i);
  19.  
  20.     // Copy the vertices to an VertexPositionColor array for VertexBuffer
  21.     VertexPositionColor[] vertexData = new VertexPositionColor[this.totalVertices];
  22.     verticesList.CopyTo(vertexData);
  23.  
  24.     // Copy the indices to an array
  25.     short[] indexData = new short[this.totalIndices];
  26.     indicesList.CopyTo(indexData);
  27.  
  28.     // Initialize the VertexBuffer, and insert the data
  29.     this.vertexBuffer = new VertexBuffer(this.GraphicsDevice, VertexPositionColor.SizeInBytes * this.totalVertices, ResourceUsage.None);
  30.     this.vertexBuffer.SetData(vertexData);
  31.  
  32.     // Initialize the IndexBuffer, and insert the data
  33.     this.indexBuffer = new IndexBuffer(this.GraphicsDevice, sizeof(short) * this.totalIndices, ResourceUsage.None, IndexElementSize.SixteenBits);
  34.     this.indexBuffer.SetData(indexData);
  35. }

Notice that I've used .NET's collection classes to keep the vertices (verticesList) and indices (indicesList) throughout the script, and that I convert them to arrays at the bottom. The reason I'm doing this, is because adding and removing elements from a collection type is easier than from an array type. I've scaled the World matrix with a thousand units to get a better view of the vertices, but they will basically be positioned like this:

Allright, so lets implement the algorithm that actually draws the torus. The first thing we want, are the five locations of the segments. Which comes down to the multiplication of each segment with one fifth of a circle. I hope the following image illustrates this properly.

Let look at this in the code shall we? To calculate the size of our segment, we divide a full circle in radians (2 of PI) by the number of segments. In the loop, we multiply the size with the index of each segment, and because those values are still radians, we convert them to their cosine and sine angles. Finally we multiply this value with our segment radius because it is going to be hard to scale the object later.

C#:
  1. // Save these locally as floats
  2. float segmentRadius = this.SegmentRadius;
  3. float numSegments = this.Segments;
  4.  
  5. // Calculate size of segment
  6. float segmentSize = 2 * MathHelper.Pi / numSegments;
  7.  
  8. // Loop through number of tubes
  9. for (int i = 0; i <numSegments; i++)
  10. {
  11.     // Calculate X, Y, Z coordinates..
  12.     x = segmentRadius * Math.Cos(i * segmentSize);
  13.     y = segmentRadius * Math.Sin(i * segmentSize);
  14.  
  15.     // Create a new vertex element
  16.     VertexPositionColor vertex = new VertexPositionColor(new Vector3((float)x, (float)y, (float)z), Color.White);
  17.  
  18.     // Add the vertex to global vertex list
  19.     verticesList.Add(vertex);
  20. }

Now we're getting somewhere. We already have the five segments at the correct locations. Next? Create another loop that calculates the positions of the tube!

C#:
  1. // Calculate X, Y, Z coordinates..
  2. x = (segmentRadius + tubeRadius * Math.Cos(j * tubeSize)) * Math.Cos(i * segmentSize);
  3. y = (segmentRadius + tubeRadius * Math.Cos(j * tubeSize)) * Math.Sin(i * segmentSize);
  4. z = radius * Math.Sin(j * tubeSize);

As you can see I've simply added the cosine angle of the tube to the radius of the segment. I've multiplied it before multiplication with the segment itself because the X and Y positions of the Vertex are affected by the tube. With this in place, I think it's safe to increment the number of tubes and segments to see how the code performs. The following screenshot shows the torus vertices with 25 segments and tubes.

Finally, after all the work we did there is some result. Believe me, without all of the preperation we did, things would have become complicated quite faster. So what's left to do? Well, we still need to fill our IndexBuffer with triangle data. However, the triangles will always contain two vertices from the a segment, and one vertex from the next. To be able to loop through the segments and tubes, I've created two small collectionz that contain the vertices per tube (tubeList), and the tubes per segment (segmentList). I'm still keeping the verticesList collection because that one is already finished. Notice how I added code to the loops that save each tube vertex to the tubeList, and each tubeList to the segmentList.

C#:
  1. // Init temp lists with tubes and segments
  2. List segmentList = new List();
  3. List tubeList = new List();
  4.  
  5. // Loop through number of tubes
  6. for (int i = 0; i <numSegments; i++)
  7. {
  8.     tubeList = new List();
  9.  
  10.     for (int j = 0; j <numTubes; j++)
  11.     {
  12.         // Calculate X, Y, Z coordinates..
  13.         x = (segmentRadius + tubeRadius * Math.Cos(j * tubeSize)) * Math.Cos(i * segmentSize);
  14.         y = (segmentRadius + tubeRadius * Math.Cos(j * tubeSize)) * Math.Sin(i * segmentSize);
  15.         z = tubeRadius * Math.Sin(j * tubeSize);
  16.  
  17.         // Create a new vertex element
  18.         VertexPositionColor vertex = new VertexPositionColor(new Vector3((float)x, (float)y, (float)z), Color.White);
  19.  
  20.         // Add the vertex to the tubeList
  21.         tubeList.Add(vertex);
  22.  
  23.         // Add the vertex to global vertex list
  24.         verticesList.Add(vertex);
  25.     }
  26.  
  27.     // Add the filled tubeList to the segmentList
  28.     segmentList.Add(tubeList);
  29. }

You're really getting excited now are you? Well the end is near... All that is remaining is filling the IndexBuffer with triangle data, and now that we have a nice collection with segments and tubes it isn't really rocketscience any more. The code is pretty straightforward aswell. First, we loop through our segments and determen the next segment in the torus. Then I loop through the vertex elements of the tube and save the indices of the first two vertex elements of the first segment, and one vertex element of the next segment. See that in this case it was easier to implement the IndexBuffer generation code in the InitializeVertices method?

C#:
  1. // Loop through the segments
  2. for (int i = 0; i <segmentList.Count; i++)
  3. {
  4.     // Find next (or first) segment offset
  5.     int n = (i + 1) % segmentList.Count;
  6.  
  7.     // Find current and next segments
  8.     List currentTube = segmentList[i];
  9.     List nextTube = segmentList[n];
  10.  
  11.     // Loop through the vertices in the tube
  12.     for (int j = 0; j <currentTube.Count; j++)
  13.     {
  14.         // Find next (or first) vertex offset
  15.         int m = (j + 1) % currentTube.Count;
  16.  
  17.         // Find the 4 vertices that make up a quad
  18.         VertexPositionColor v1 = currentTube[j];
  19.         VertexPositionColor v2 = currentTube[m];
  20.         VertexPositionColor v3 = nextTube[m];
  21.         VertexPositionColor v4 = nextTube[j];
  22.  
  23.         // Draw the first triangle
  24.         indicesList.Add((short)verticesList.IndexOf(v1));
  25.         indicesList.Add((short)verticesList.IndexOf(v2));
  26.         indicesList.Add((short)verticesList.IndexOf(v3));
  27.  
  28.         // Finish the quad
  29.         indicesList.Add((short)verticesList.IndexOf(v3));
  30.         indicesList.Add((short)verticesList.IndexOf(v4));
  31.         indicesList.Add((short)verticesList.IndexOf(v1));
  32.     }
  33. }

Pretty easy huh? Lets change the type of primitives we draw to triangleList, and set the FillMode of the RenderState to draw a wireframe mesh. Don't forget to change the number of primitives we want to draw, because there are twice as much triangles as vertices in the mesh. Final screenshot? Enjoy!!

The code of this component can be found in the following solution; 07 - Drawing a complex mesh

Microsoft XNA Framework; Drawing primitives

Allright, if you’ve been paying attention to the previous articles, you should now have a basic understanding of how 3d mathematics work and how they are applied in the XNA framework. We’ve created gamecomponents and a freelook camera that responds to user input. We’ve also used the content pipeline to load a 3d model into [...]

Dream.Build.Play();

Your dream game - build it today - millions may play it tomorrow!
Â
The XNA team announced a global contest for all XNA developers last week, using Dream.Build.Play as the brand. They've launched a new website at dreambuildplay.com which will be updated somewhere in January, around the same time the contest begins. Go ahead and navigate [...]

Microsoft XNA Framework; Creating a freelook camera

Before we continue with some real exciting stuff, we'll need to create a more interactive environment for us to navigate through. Right now, every 3d model in the world space is transformed to the screen using its own View and Projection matrices. These matrices define the Viewing Frustum of the scene (projection matrix), and the [...]

Xbox 360 HDMI

Apparently there is a reward in place for all of you that don't own a Xbox 360 console yet, it looks like the next-generation consoles will be equipped with a HDMI port on the back. Of course, Microsoft still refuses to acknowlegde this, but there are good reasons to believe it to be true.

Â
Check out [...]

Microsoft XNA framework; GameComponents and GameServices

First of all let me start by wishing everyone reading this blog a happy new year, here's to a great 2007 with lots of XNA goodness
The last post on XNA was about loading a 3d model using the content pipeline. We did this by adding the model to our project at design time, [...]

Microsoft XNA Framework; Loading a 3d model

All right, so lets do something a little more exciting for a change, this entry will demonstrate how to render a 3d model from the MySpaceWar starter kit in an XNA application using the Content Pipeline.
The first we thing we need before we can start coding, are some assets for our game. In this case, [...]

VS.NET 2005; Visualizers

With all the Visualizer madness going on, I thought it would be a nice idea to create some Visualizers for Microsoft Visual C# Express and GSE. Mike Manders already created a nice Matrix Visualizer which I use frequently, but I still needed one to visualize my textures, especially if I want to check which texture [...]

Microsoft XNA Framework; Expanding MeasureFPS

All right, let us continue with adding some visuals to our MeasureFPS GameComponent. It currently raises an event every second that passes the number of frames that have been rendered in the previous second. However we are going to add some logic to the component which draws this number on the screen.
Let’s start by deriving [...]

Microsoft XNA Framework; Creating GameComponents

To continue where I left off previously, lets talk a little about GameComponents. As stated before, GameComponents provide a modular for adding functionality to a game, without piling up lines of code in the game class itself. To do so, you simple need to add a GameComponent object to the Components collection of the game.
Basically [...]