All of the code we've written thus far concentrated on drawing a single type of primitive, namely a list of triangles. However, there are actually numerous different primitive types we can draw. The following list describes these primitive types:
We can use the same vertex data to draw any of these types of primitives. Direct3D will interpret the vertex data differently depending on the type of primitive that you tell it will be drawn. Let's write a quick bit of code to draw each of the different primitive types. The sample code on the CD was derived from the first vertex buffer sample we created in Chapter 3, "Rendering Using Simple Techniques." The changes made to the code will be outlined here. Since this code will not require our vertices to be moved, the world transform in SetupCamera was removed, as well as all references to the private "angle" member variable. The following constant was then added: private const int NumberItems = 12; Twelve was picked arbitrarily, but for a reason. Too many vertices, and the screen gets cluttered; however, we want to ensure we have a number of vertices that is both even and divisible by three. This is so both our LineList primitives and our TriangleList primitives can be rendered correctly. We next need to modify the vertex buffer creation and the OnVertexBufferCreate function as in Listing 4.1: Listing 4.1 Creating Random Verticesvb = new VertexBuffer(typeof(CustomVertex.PositionColored), NumberItems, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format, Pool.Default); // Create a list of vertices equal to the number of items CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[NumberItems]; // Randomly select the position of this vertex.. Keep it within a range of 5. for(int i = 0; i < NumberItems; i++) { float xPos = (float)(Rnd.NextDouble() * 5.0f) - (float)(Rnd.NextDouble() * 5.0f); float yPos = (float)(Rnd.NextDouble() * 5.0f) - (float)(Rnd.NextDouble() * 5.0f); float zPos = (float)(Rnd.NextDouble() * 5.0f) - (float)(Rnd.NextDouble() * 5.0f); verts[i].SetPosition(new Vector3(xPos, yPos, zPos)); verts[i].Color = RandomColor.ToArgb(); } buffer.SetData(verts, 0, LockFlags.None); There is nothing magical going on here. We modified our vertex buffer to hold the correct number of vertices (as defined by our NumberItems constant). We then modified our vertex creation function to plot the correct number of vertices in a random way. You can see the source code included on the CD for the definitions of "Rnd" and "RandomColor". Now we need to modify our actual drawing calls. An easy way to show the difference in how these various primitive types interact with one another would be to have the code scroll through them sequentially. Let's have our drawing code do that every two seconds. An easy way to do this would be to check our current time (in ticks) versus the startup time. Add the following member variables into your declaration section: private bool needRecreate = false; // Timing information private static readonly int InitialTickCount = System.Environment.TickCount; The Boolean value will allow us to re-create our vertex buffer at the start of each "cycle," so we don't continue to display the same vertices over and over again. Replace the single DrawPrimitives call with the code section in Listing 4.2: Listing 4.2 Rendering Each Primitive Type// We will decide what to draw based on primitives int index = ((System.Environment.TickCount - InitialTickCount) / 2000) % 6; switch (index) { case 0: // Points device.DrawPrimitives(PrimitiveType.PointList, 0, NumberItems); if (needRecreate) { // After the primitives have been drawn, recreate a new set OnVertexBufferCreate(vb, null); needRecreate = false; } break; case 1: // LineList device.DrawPrimitives(PrimitiveType.LineList, 0, NumberItems / 2); needRecreate = true; break; case 2: // LineStrip device.DrawPrimitives(PrimitiveType.LineStrip, 0, NumberItems - 1); break; case 3: // TriangleList device.DrawPrimitives(PrimitiveType.TriangleList, 0, NumberItems / 3); break; case 4: // TriangleStrip device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, NumberItems - 2); break; case 5: // TriangleFan device.DrawPrimitives(PrimitiveType.TriangleFan, 0, NumberItems - 2); break; } This is a relatively self-explanatory section of code. Depending on which primitive type we are on in our cycle, we simply call DrawPrimitives with the appropriate type. You'll notice that for a list of points, we simply use the number of vertices as our primitive count, drawing one point for each vertex. LineList and TriangleList primitives draw isolated primitives, so our DrawPrimitives call with these types is the number of vertices divided by the number of vertices in a single primitive of the correct type: two or three respectively. For LineStrip primitives, given that each vertex after the second shares its starting point with the ending point of the previous line, you only need to draw one less than the current number of items. TriangleStrip and TriangleFan primitives behave the same way, but since there are three vertices in a triangle, with two shared, we use two less. Running this application will show you first a series of points rendered on the screen, followed by a series of disjointed lines. Next these lines will connect to form a solid polyline, and then switch to a series of isolated triangles. Finally, these triangles will begin sharing vertices to form a strip, and then a fan. One thing you may notice is that the "points" may be small and hard to see. You can artificially "scale" these points by setting a render state. Add the following line to your SetupCamera function to scale each point to three times its normal size: device.RenderState.PointSize = 3.0f; You'll notice that the single points are now much larger than they were originally. Feel free to play with this render state. Now that we have the different types of primitives drawing correctly, let's investigate ways to lower the amount of memory usage of our data. |