Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Managed DirectX Tutorials: Part 3 - Rendering Primitives

0.00/5 (No votes)
3 Feb 2006 1  
This is the third in a series of tutorials designed to show you how to create a basic terrain engine.

Introduction

This is the third in a series of tutorials which will allow you to create your own game engine (from initialization, to a fully rotatable, height mapped 3D world!).

In this tutorial, we will use Direct3D to create and render a primitive, as well as learn some more DirectX concepts.

Background

The code from these tutorials is taken from my own game engine: MAGEngine.NET, at different stages in development.

Using the code

You may use all of the code I provide as how you see fit, except to create another tutorial. You can use it as a sturdy(ish ;)) framework for your own applications, or print loads of copies off so that when I'm rich and famous you can sell them for $100 each ;)

Prerequisites

To do this series of tutorials, you will need:

  • C# Compiler (preferably Visual C# 2005 Express)
  • Managed DirectX 9.0 October SDK

OK, in the last tutorial, we spent a long time discussing various aspects of the Direct3D component, we created a good, solid sample framework, but we never got to render anything � which is the whole concept of Direct3D. In this tutorial, we will:

  • Learn about primitives
  • Initialize a triangle
  • Set transforms
  • Render the triangle
  • Create a cObject class to use in our game

Theory

All 3D worlds - whether it is a character, terrain, or a box - are made out of triangles. And all these triangles are essentially three points, or vertices. So, to render our triangle, we will need to define three of these "points" and send them to be rendered in our OnPaint function. In Managed DirectX, there is a set of structures made to represent different types of these points, known as the CustomVertex class.

From this you, can choose a "point" structure to suit your needs - if you want a textured box which you could rotate, then you would choose PositionTextured; if you want that box to be shaded, then you would choose PositionNormalTextured. For today, we will be creating a static, colored triangle, so we will be using CustomVertex.TransformedColored points.

Transformed indicates that the coordinates we define have been declared as screen coordinates, and do not need to be scaled, rotated, or otherwise.

Now, your initial reaction may be to jump straight into the code and render your triangle - but first, we should create a class to help us with this.

We will be making two classes, one called cObject which contains an array of CustomVertex.TransformedColored(s?), and a bool indicating if we should render the triangle (IsActive). We will then make a class representing a triangle. It will inherit cObject, but will have two functions: an initialization function where it defines the array and any coordinates, and its own render function.

In Direct3D, the best way to render is to use the OnPaint event, as we did in the previous tutorial. But, it will make life a lot easier in the future if we can set up all the framework in OnPaint, and just call this render method. We could even do it within a for() loop for arrays of triangle objects.

The above classes will be declared in a new file called cObject, and are as follows:

public class cObject
{
  public CustomVertex.TransformedColored[] itsVertices;
}

public class cTriangle : cObject
{
  bool isActive;
  //Should this primitive be rendered?

  
  public cTriangle(CustomVertex.TransformedColored[] itsVerts)
  {
      itsVertices = new CustomVertex.TransformedColored[3];
      isActive = true; itsVertices[0] = itsVerts[0];
      //Assign the parameter vertices to that of the object

      itsVertices[1] = itsVerts[1];
      itsVertices[2] = itsVerts[2];
  }
  
  public void Render(Device device)
  {
      device.DrawUserPrimitives(PrimitiveType.TriangleList, 
                                          1, itsVertices;); 
      // Generic code for the primitive to 'render itself' 

  }

As you can see, the cObject class is fairly simple - only containing an unassigned array of TransformedColored vertices. The cTriangle class only adds the isActive member, and the initialization and render functions.

The Render function, as you can see, calls the function DrawUserPrimitives(). This is a function, obviously used to render primitives. You give it a PrimitiveType - this simply tells DirectX 'how' to render the vertices. There are many examples, and displayed below you can see how they impact the positioning of the primitive:

The other parameters are fairly simple - how many primitives to render (=1) and then the source (or where the primitives to be rendered can be found). So, now that we have this class updated, we only have two more things to do to get our triangle rendered:

  1. Create a cTriangle object
  2. Initialize this object
  3. Update our OnPaint method to render the triangle

So, with this class created, it should be easy to create an instance of your class - make a public cTriangle object in your window class (I will call mine "Tri" in the samples). Now comes a slightly tricky part which we will resolve in the next tutorial.

We need to create a CustomVertex.TransformedColored array to assign to the cTriangle instance we will be creating and pass through its constructor. To do this, use the following code in your main() method, nothing should be fairly new here:

CustomVertex.TransformedColored[] Verts = 
     new CustomVertex.TransformedColored[3];
Verts[0] = new CustomVertex.TransformedColored(new 
     Vector4(100.0f, 100.0f, 0.0f, 1.0f), Color.Red.ToArgb());
Verts[1] = new CustomVertex.TransformedColored(new 
     Vector4(200.0f, 100.0f, 0.0f, 1.0f), Color.Blue.ToArgb());
Verts[2] = new CustomVertex.TransformedColored(new 
     Vector4(150.0f, 200.0f, 0.0f, 1.0f), Color.Green.ToArgb());

OK, the only thing slightly strange here should be the Vector4 structure. The last parameter is known as the RHW, which is only used in transformed coordinates as it is used in perspectives. I would highly recommend to not bother about this - we will never be using it again after this tutorial when we use positioned coordinates, and it will always be 1.0 for this tutorial. For this triangle, the Z coordinate will always be 0.0f. The ToArgb() function converts a .NET color to its Alpha-Red-Green-Blue value. For example, White, when converted to ARGB brings the value (0,255,255,255) (ignoring A which is Alpha, or transparency) because of course, white is a mixture of all the colors, whereas Red would convert to (0,255,0,0), and so on.

Now, make a call to your triangle's constructor and pass in the vertices defined above as the parameter. Then, go to the OnPaint event of your form and we will finish this tutorial.

From OnPaint, erase everything in it.

OnPaint will be called in every single frame, every time the display needs to be updated. So, we need to do a number of things:

  1. Clear the display area
  2. Notify the GPU of what type of vertices we will be rendering
  3. Lock the GPU for rendering
  4. Render our primitives
  5. Unlock the GPU
  6. Swap the old frame with the new one
  7. Call the Invalidate function for .NET

The reason for locking and unlocking the GPU during rendering is to reduce errors at run time. When we render, we render to what is called a back buffer. This is an off-screen surface where any data can be stored until we call the device.Present() function to copy it to the on screen surface.

To code, this translates as:

device.Clear(ClearFlags.Target, 
    System.Drawing.Color.Black, 1.0f, 0);
    //Clear display

device.VertexFormat = 
    CustomVertex.TransformedColored.Format;
    //Notify GPU which vertices 

device.BeginScene(); //Lock GPU

Triangle.Render(device); //Render primitives 

device.EndScene(); //Unlock GPU 

device.Present(); //Swap old with new 

this.Invalidate(); //call invalidate

This should make up the entire body of the OnPaint function.

If you compile and run this code, you should see a blended-colour triangle on your screen.

X-Challenge

  • Use the classes you created to render three triangles side by side
  • Modify the OnPaint event so that it can render all initialized triangles without us needing to edit it every time we add or remove one from the array.

Contact

Please send all emails to xpyder@magclan.cwhnetworks.com. I do have MSN Messenger, my email address for this is jamespraveen@aol.com.

History

  • 19/01/06: Submitted tutorials 1-3 to CodeProject.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here