Greg Dolley’s Weblog

A Blog about Graphics Programming, Game Programming, Tips and Tricks

  • Archives

  • April 2024
    M T W T F S S
    1234567
    891011121314
    15161718192021
    22232425262728
    2930  
  • Blog Stats

    • 1,075,912 hits
  • Subscribers

  • Twitter

DirectX 9 C++ Graphics Tutorial Part 3 – Drawing a Simple 3D Room

Posted by gregd1024 on June 15, 2008

Welcome to part three of the DirectX native C++ tutorials. In this part we’re going to look at drawing a set of 3D triangles. These triangles will make up a cube and the camera will be positioned inside of it (like you’re standing in a square room). While the last tutorial technically had a 3D triangle – after all, we specified z coordinates for every vertex – the scene was not truly 3D as far as DirectX is concerned. Try changing the z coordinate for any of those vertices and you’ll see what I mean – the triangle still doesn’t look like it’s in perspective.

Requirements

The tutorial sample code was built and tested with Visual Studio Express 2008. However, using the code “as-is” in Visual Studio 2005 should work too.

You’ll need the DirectX 9 SDK for compiling the sample code. Use this link for downloading: click here.

Tutorial Source and Project Files

Download the project files, binaries, and source with this link:

Getting Started

DirectX needs three matrices in order to render a true 3D scene – a world transformation matrix, a camera view matrix, and a projection matrix. If you’re unfamiliar with mathematical matrices, read the next section for a brief introduction. Those already seasoned in this subject can just skip it.

Introduction to Matrices

Matrices are basically mathematical tables used in linear algebra. They look like a grid and contain numbers. An example is shown below:

4_by_4_identity_matrix

This is known as a 4-by-4 matrix since it has four columns and four rows. It doesn’t necessarily have to be a square – an m-by-n matrix is quite common. However, the world, view, and projection matrices are all squares (4×4).

Just like regular numbers, matrices can be multiplied together or added to one another. A matrix like the one above is known as an identity matrix because of this special property: some matrix (let’s call it “M”) multiplied by an identity results in the same M matrix.

For this tutorial I’m going to show you how to multiply a m-by-m matrix by a m-by-1 matrix. If you’d like to know about the other rules for matrix addition and multiplication, search for “matrix mathematics” on Wikipedia. For this tutorial, however, it would only be helpful to know the procedure behind multiplying a square matrix by a one column matrix. Here’s how it would be done given a 2-by-2 and 2-by-1 matrix:

matrix_mult_2_by_2_and_2_by_1 

A 3-by-3 matrix multiplied by a 3-by-1 matrix would look like this:

matrix_mult_3_by_3_and_3_by_1 

For the sake of discussion, let’s call the bigger matrix “A” and the one column matrix “B.” The values in each row of A are multiplied by the values in B and the results are summed making a matrix with the same dimensions as B.

So why is this useful? Well, for one, we can represent a lot of common formulas in a table format as opposed to writing them out long-hand. For example, the regular way of expressing the 2D point rotation formula is this:

x_y_rotation_formulas

Now if we put the sine and cosine coefficients into a 2-by-2 matrix and put the x and y coordinates into a 2-by-1 matrix, we can represent the above formula like this instead:

x_y_rotation_formula_in_matrix

Try multiplying these two matrices as described above and you’ll find that it evaluates to exactly the same as the long-hand version. In other words, we can rotate any 2D point just by multiplying it with this 2-by-2 matrix! The same holds true for 3D points – we multiply them against a 3-by-3 matrix to get the newly rotated point (however, the 3-by-3 rotation matrix is much more complicated than its 2D counterpart as you’ll see shortly).

The reason that matrices are used in many 3D graphics applications, and what makes them unique, is the fact that fairly complex mathematical calculations can be simplified with just one or two matrix computations. For example, if you wanted to rotate a line segment around the z-axis in 3D space, you could do it via a brute-force method – apply the same long-hand trigonometric rotation equations to each point:

x_y_rotation_formulas_with_z

Note that the z component stays the same. To take the resulting vector and then rotate it around the y-axis, you’d use the same equations but apply them to the x and z components of each point:

x_z_rotation_formulas_with_y

Note that this time the y component stays the same and that x’ (the result of rotation #1) is used to calculate the new position instead of the original x. To do a third rotation, around the x-axis, the same equations would apply except Zrot and Yrot would be used as inputs (Xrot would stay the same).

Now here’s the drawback to doing it brute-force versus using a matrix: we’re always rotating around one of the three coordinate system axes. What if we want to rotate a point around some arbitrary vector instead of the x, y, or z axis? Using the long-hand equations to do that gets extremely complicated. However, there is one kind of matrix you can construct that, if multiplied by a 3-by-1 matrix representing a point, will result in a new 3-by-1 matrix containing values for the rotated point!

In case you’re wondering, this special type of matrix is called an angle/axis matrix and it looks like this:

angle_axis_matrix 

It may look scary at first, but you really only need to plug in two things: the components for vector V (which is the vector you want to rotate the point around) and Θ (the angle you want the point rotated). Note, however, that vector V must be normalized (it’s length must equal 1) for this matrix to work.

There’s another special property to this matrix – after plugging in your angle/vector combination and calculating the number values for each element, those numbers actually represent vectors themselves – each column corresponds to a vector (so three total). These three vectors have one special property – they’re always perpendicular to each other. That’s right, no matter what angle or V vector you plug in, you’ll always end of with three vectors that are aligned just like the x, y, z coordinate system axes. But there’s more! If you were to rotate the coordinate system axes around vector V by your angle, the new coordinate system axis vectors would match the vectors contained in each column of this matrix! Can you see now what this matrix is really doing?

It’s called an angle/axis matrix because you’re actually calculating how the coordinate system axes would be oriented if you rotated them by a given angle around a certain vector. Considering these properties, you could rewrite the above matrix like this:

3_by_3_xyz_vector_matrix  

The X vector, column one, holds the new x-axis after rotation. Column two, vector Y, holds the new y-axis after rotation. And as you can probably guess, column three, vector Z, holds the new z-axis after rotation.

Rotation is not the only type of operation that can be done with matrices – you can also translate a point (move it to a new position). A typical translation matrix looks like this:

translation_matrix 

The Tx, Ty, and Tz components in the fourth column determine how much to move along the x, y, and z axes respectively. Since this is a 4-by-4 matrix, you need to change the dimensions of the point matrix into 4-by-1. You do this by just inserting 1 into the fourth row. In other words, the entire operation would look like this:

translation_matrix_times_point 

Now you’re about to see the second beauty of matrices and why they’re really used in 3D graphics: if you want to rotate and translate a point, you can actually combine the rotation matrix with the translation matrix! The new matrix becomes:

translation_matrix_times_point_result 

With just this one matrix you can specify not only how much to rotate a point, but also how to translate that point as well! A matrix which combines rotation and translation is called a “transformation” matrix. As you can probably guess, this type of matrix moves a 3D world around the player.

Now that you’ve had an introduction to matrices and how a transformation matrix works, we can move on to the rest of the tutorial.

About the Code

As usual we’re going to build off of the example program from the last tutorial. Details about creating a window, setting up DirectX, and drawing a 2D triangle will not be covered here. If you need to know about those things, then I suggest reading the previous tutorial before moving on. Also, the example program for this segment uses DirectInput to capture keystrokes from the user. I won’t be covering details on DirectInput, but those parts of the code are clearly marked.

Also, unlike some of the previous posts, I won’t include a full listing – the code has simply grown too large. However, I will go over all the pertinent parts so you can learn the most from this tutorial. Anything that was changed or modified will be shown.

Going from 2D to 3D

The code in the last tutorial drew a simple 2D triangle on the screen. Even though we specified a z-coordinate for each vertex of that triangle, it was still not really 3D. Changing the z-coordinate for any of the vertices still didn’t draw the triangle in perspective. Why is this?

The vertex type used was D3DFVF_XYZRHW – the type used for pre-transformed vertices. DirectX expects these vertex types to have already gone through a 3D transformation pipeline. Once through the pipeline, z-coordinates become irrelevant – only screen coordinates (x and y) are used.

In order to render a true 3D scene, we need to add and/or change two things:

  • Feed the following matrices to DirectX so it knows how to transform the vertices from world space to the screen: world transformation matrix, view matrix, and projection matrix.
  • Change the vertex types to D3DFVF_XYZ – these vertices are untransformed and relative to world space.

Adding the Transformation Matrices

Before getting into the code, let’s briefly go over the purpose of each matrix. As the last section mentioned, DirectX needs the following three matrices:

  • The world transformation matrix.
  • The view transformation matrix.
  • The projection transformation matrix.

The world transformation matrix tells DirectX how to rotate, translate, and possibly scale 3D model coordinates into world space. In a typical 3D video game, polygonal models are the objects which tend to be reused in different parts of the world (weapons, bonus items, enemy players, monsters, etc.). Their vertices are defined relative to their own local coordinate system. The world transformation matrix converts these local coordinates into absolute positions in the 3D world (hence the name).

The view matrix (sometimes called the camera matrix, or camera-view matrix) tells DirectX how to transform world coordinates into camera coordinates (basically, where are you and what are you looking at). The world coordinates become relative to the camera axes after this matrix is applied.

If you have some experience in 3D graphics programming, don’t confuse the world matrix with the view matrix. Many tutorials and books about graphics sometimes refer to the world matrix and view matrix as one of the same. This is due to a certain optimization that can be done where you combine the world matrix and the view matrix into one master transformation matrix resulting in just one matrix update per frame instead of two.

The projection matrix tells DirectX about your 2D viewport into the 3D world. It holds the following information about the screen and camera: field of view, aspect ratio of the screen, how far the camera can see (far clipping plane), and how near the camera can see (near clipping plane).

The process of creating a world, view, and projection matrix isn’t difficult – if you use the Direct3D Extensions Utility library. Among other things, this library contains a few useful functions that return fully populated matrices given certain parameters. For example, provide an angle for the D3DXMatrixRotationY() function, and it will return a world transformation matrix that does rotation around the y-axis. If you ever need to calculate these matrices yourself, without the library’s help, you can refer to DirectX’s SDK documentation – it contains the layout and formulas for each matrix.

The order in which you feed these matrices to DirectX is irrelevant – although it internally applies them to the scene in the same order (world -> view -> projection). Since the order doesn’t matter, we set the projection matrix at initialization time and then forget about it. This matrix would only need to change if the screen’s aspect ratio, field of view, or clipping planes were altered.

We add the following code in order to create and set the projection matrix:

D3DXMATRIXA16 ProjectionMatrix;

D3DXMatrixPerspectiveFovLH(&ProjectionMatrix, PI/4, 1.0f, 1.0f, 500.0f);

g_pDirect3D_Device->SetTransform(D3DTS_PROJECTION, &ProjectionMatrix);

The call to D3DXMatrixPerspectiveFovLH() creates a projection matrix given the following values:

  • Field of view (in radians).
  • Aspect ratio.
  • Z-value of the near clip plane.
  • Z-value of the far clip plane.

These values go in parameters two through five, respectively. The first parameter holds a pointer to the matrix object which will receive the result.

The last line calls SetTransform(). This function is used to feed DirectX all the different types of matrices. The first parameter distinguishes which matrix type you want to set. D3DTS_PROJECTION indicates a projection matrix is contained in the second parameter.

Next we create the world transformation matrix. This is also set at initialization time. Why? Our example program has no 3D polygonal models and therefore doesn’t need to use it. As such, we simply send DirectX an identity matrix so it doesn’t affect any of the math in the 3D pipeline. Here’s what that code looks like:

D3DXMATRIXA16 WorldTransformMatrix;

D3DXMatrixIdentity(&WorldTransformMatrix);

g_pDirect3D_Device->SetTransform(D3DTS_WORLD, &WorldTransformMatrix);

We initialize the world transform matrix to an identity with the D3DXMatrixIdentity() function and then call SetTransform() just as we did with the projection matrix. The first parameter, D3DTS_WORLD, tells DirectX to use this matrix as the world transform. One thing to note: instead of calling D3DXMatrixIdentity(), we could have easily set the matrix manually through the object’s constructor:

D3DXMATRIXA16 WorldTransformMatrix(1, 0, 0, 0,

                                   0, 1, 0, 0,

                                   0, 0, 1, 0,

                                   0, 0, 0, 1);

I used the function call instead for clarity, but both methods are equivalent.

Now for the view/camera matrix. This one we must set on every frame since the direction of the camera can change at any time. If you take a look at the code, you’ll notice a function named CalcMatrices() – this is where the matrix is being populated. That code looks like:

void CalcMatrices(void)

{

   D3DXMATRIXA16 ViewMatrix;

  

   // set the view matrix

   D3DXVECTOR3 EyePoint(g_Camera.Location.x,

                        g_Camera.Location.y,

                        g_Camera.Location.z);

   D3DXVECTOR3 LookAt(g_Camera.Location.x+cos(g_Camera.Rotation),

                      g_Camera.Location.y,

                      g_Camera.Location.z+sin(g_Camera.Rotation));

   D3DXVECTOR3 UpVector(0.0f, 1.0f, 0.0f);

   D3DXMatrixLookAtLH(&ViewMatrix, &EyePoint, &LookAt, &UpVector);

  

   g_pDirect3D_Device->SetTransform(D3DTS_VIEW, &ViewMatrix);

}

In this function we must give DirectX two points and something called an “up” vector. The first point is the camera’s position – its location in 3D space. The second is any point along the camera’s direct line-of-sight – in other words, any point that, if you were to look through the camera, would be centered in the camera’s field of view. DirectX simply uses these two points to calculate the camera’s view vector – what direction the camera is pointing. Since the look-at point can be any point along the camera’s line-of-sight, I just use the sine and cosine functions to calculate some point directly in front of the camera. The y-coordinate doesn’t change because, in this example program, I’ve tried to keep things simple and not allowed the camera to look up or down (only side-to-side). The “up” vector defines which direction points directly up from the camera’s point of view.

You may be wondering why the up-vector is needed if we already have two points describing the camera’s direction. Here’s why: suppose that the camera is looking straight ahead, directly down the z-axis. Now turn the camera up-side down (in other words, rotate it 180 degrees around the z-axis). Did the camera’s viewing direction change? Nope. What if the camera was turned side-ways (or rotated 90 degrees around the z-axis)? Even then, the viewing direction doesn’t change (it’s still looking down the z-axis). So using just two points gives enough information to know where the camera is pointing, but it doesn’t describe the “roll” of the camera relative to itself. In this tutorial I haven’t allowed the camera to roll over, so the up-vector stays at (0, 1, 0) – in other words, the camera can’t look up or down.

Once we’ve created the camera point, look-at point, and up-vector, we pass all of them to D3DXMatrixLookAtLH() – a function that calculates a view/camera matrix and puts it into the first parameter, ViewMatrix.

Finally we call SetTransform() to feed DirectX our newly calculated matrix. The first parameter, D3DTS_VIEW, tells DirectX to use this matrix as the view/camera matrix. The second parameter is a pointer to the matrix itself.

The Rendering Loop

Now that all the matrices have been set, we’re ready to tackle the main rendering loop. Just as in the last tutorial, we begin by setting up the vertex format structure:

struct D3DVERTEX {float x, y, z; DWORD color;} vertices[NUM_VERTICES];

This time we don’t need the “rhw” component. We’re feeding DirectX non-transformed vertices so therefore a “w” component doesn’t apply. DirectX just needs the (x, y, z) components and the color of each vertex.

We then fill in the “vertices” array. Here’s an example of the first point:

vertices[0].x = -64.0f*3;

vertices[0].y = -64.0f;

vertices[0].z = 0;

vertices[0].color = FRONT_WALL_COLOR;

<…more vertices here…>

Once we’ve filled in all the vertices, we must feed them to DirectX. The code which feeds DirectX is exactly the same as before (see last tutorial) with one exception – the vertex format is different.

LPDIRECT3DVERTEXBUFFER9 pVertexObject = NULL;

void *pVertexBuffer = NULL;

 

if(FAILED(

g_pDirect3D_Device->CreateVertexBuffer(NUM_VERTICES*sizeof(D3DVERTEX), 0,

D3DFVF_XYZ|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVertexObject, NULL)))

   return;

 

if(FAILED(pVertexObject->Lock(0, NUM_VERTICES*sizeof(D3DVERTEX), &pVertexBuffer, 0)))

   return;

 

memcpy(pVertexBuffer, vertices, NUM_VERTICES*sizeof(D3DVERTEX));

pVertexObject->Unlock();

The vertex type, D3DFVF_XYZRHW, gets replaced with D3DFVF_XYZ because we’re feeding in (x, y, z) components instead of (x, y, z, 1/w). I’m not going to go into the inner-workings on this code because it was already covered in the last tutorial.

Now we’re ready to render the scene. Again, this code looks much like the last tutorial except the vertex format has changed, plus, we’re now calculating the transformation matrices before rendering.

g_pDirect3D_Device->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); // clear frame

 

if(SUCCEEDED(g_pDirect3D_Device->BeginScene()))

{

   CalcMatrices();

   g_pDirect3D_Device->SetStreamSource(0, pVertexObject, 0,

   sizeof(D3DVERTEX));

   g_pDirect3D_Device->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE);

   g_pDirect3D_Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, NUM_VERTICES/3);

   g_pDirect3D_Device->EndScene();

}

 

g_pDirect3D_Device->Present(NULL, NULL, NULL, NULL);

pVertexObject->Release();

This time around, when we call SetFVF(), we supply the (x, y, z) vertex format instead of (x, y, z, 1/w) using the D3DFVF_XYZ constant as opposed to D3DFVF_XYZRHW. Also, we call CalcMatrices() before drawing any primitives so DirectX knows which transformations to apply to the scene. The rest of the code behaves exactly the same as the last tutorial, so I’m not going to cover it again.

Handling Keyboard Input

The tutorial program uses DirectX/DirectInput to capture keyboard actions, but I’m not going to cover how DirectInput works here. Instead, I’m going to show how the program reacts to different key presses.

The function HandleKeys() is called on every frame and is responsible for updating the global camera position tracking variables depending on the keyboard state.

void HandleKeys(void)

{

   float RotationStep = PI/175.0f;

   float WalkStep = 3.0f;

 

   //——————————————-

   // adjust the camera position and orientation

   //——————————————-

   if(dx_keyboard_state[DIK_UP]&0x80) // moving forward

   {

      g_Camera.Location.x += cos(g_Camera.Rotation)*WalkStep;

      g_Camera.Location.z += sin(g_Camera.Rotation)*WalkStep;

   }

 

   if(dx_keyboard_state[DIK_DOWN]&0x80) // moving backward

   {

      g_Camera.Location.x -= cos(g_Camera.Rotation)*WalkStep;

      g_Camera.Location.z -= sin(g_Camera.Rotation)*WalkStep;

   }

 

   if(dx_keyboard_state[DIK_LEFT]&0x80) // look left

   {

      g_Camera.Rotation += RotationStep;

 

      if(g_Camera.Rotation > PI*2)

         g_Camera.Rotation = g_Camera.Rotation-PI*2;

   }

 

   if(dx_keyboard_state[DIK_RIGHT]&0x80) // look right

   {

      g_Camera.Rotation -= RotationStep;

 

      if(g_Camera.Rotation < 0)

         g_Camera.Rotation = PI*2+g_Camera.Rotation;

   }

 

   if(dx_keyboard_state[DIK_W]&0x80) // strafe left

   {

      float SideStepAngle = g_Camera.Rotation+(PI/2.0f);

 

      if(SideStepAngle > PI*2) // handle wrap-around

         SideStepAngle = SideStepAngle-PI*2;

 

      g_Camera.Location.x += cos(SideStepAngle)*WalkStep;

      g_Camera.Location.z += sin(SideStepAngle)*WalkStep;

   }

 

   if(dx_keyboard_state[DIK_E]&0x80) // strafe right

   {

      float SideStepAngle = g_Camera.Rotation-(PI/2.0f);

 

      if(SideStepAngle < 0) // handle wrap-around

         SideStepAngle = PI*2+SideStepAngle;

 

      g_Camera.Location.x += cos(SideStepAngle)*WalkStep;

      g_Camera.Location.z += sin(SideStepAngle)*WalkStep;

   }

}

Walking forward and backward is a simple matter of updating the camera’s (x, z) position. We don’t update the y-coordinate because there’s no way to “jump” or float in the air for this example. If we were using a polar coordinate system (where point positions are based on angle and ray length), moving the camera forward or backward would be easy – just increase or decrease the ray length. But since we’re in the rectangular coordinate system (where point positions are determined by x, y, and z), we must convert this increase or decrease of ray length into the (x, z) equivalent. We do this with the sine/cosine functions and then add the result to the camera’s last position in order to get the new position (or subtract from the camera’s last position if we’re moving backwards). I’m not going to get into the basics of simple trigonometry, but if you want a detailed explanation of how these trajectory formulas work, email me through the Contact page.

Strafing, or side-stepping, is done just like moving forward or backwards except the angle used in the calculation is 90 degrees plus or minus the camera’s real angle. If you’re moving left the angle is plus 90, and if you’re moving backwards, the angle is minus 90.

For looking left or right we just add or subtract from the camera’s current angle to get the new angle. However, we must check to make sure the angle hasn’t “overflowed” or gone below zero. After all, a circle has a maximum of 360 degrees – so rotating by 370 degrees is really the same as rotating by just 10 degrees. Same goes for the other side – rotating by negative 10 gets you to the same point as +350.

All this updating of the camera’s global position/orientation object is eventually used by the CalcMatrices() function in order to create a new view matrix on every frame. In other words, DirectX always has the most current camera position and renders the room based on the camera position controlled by the keyboard.

Screenshot

Here’s what the output looks like:

directx_tutorial_3_room_screenshot

Conclusion

Wow, this post ended up being _way_ longer than expected! Anyway, if you have any questions about any of the material covered, please post a comment or send me an email through my Contact page.

Thanks for reading! 😉

-Greg Dolley

Posted in 3D Graphics, 3D Mathematics, C++ Graphics, DirectX | 40 Comments »

Managed DirectX C# Graphics Tutorial 2: Drawing a Triangle

Posted by gregd1024 on February 29, 2008

Now we’re going to look at drawing a triangle with Managed DirectX (MDX 1.1) using C#. The material presented here will strive to be the equivalent of the last tutorial which was in C++.

Requirements

The tutorial sample code was built and tested on Visual Studio 2008 Express. However, the code should work with 2005. You’ll also need the DirectX SDK August 2007 or earlier. I use June 2007 – you can download it with this link.

Tutorial Source and Project Files

To get the finished result of what this tutorial teaches – project files, binaries, and source – use this link:

Getting Started

Everything involved with setting up the project, form, and DirectX will not be covered in this tutorial since it was already explained last time. See the “Managed DirectX C# Graphics Tutorial 1” if you want details on that.

In this post we’re going to take the code from the last tutorial and build off of it. We actually only need to change one function – OnPaint(). If you have the last tutorial’s project open, you can delete the contents of OnPaint. We’re going to replace those lines with stuff from the next section.

Define Vertices and Render

Unlike using DirectX in C++, there’s no need to define your own FVF vertex structure. Managed DirectX has pre-defined vertex types. The one we need to use in order to draw our triangle is represented by the “TransformedColored” struct. This struct is a nested entity inside the CustomVertex class. The CustomVertex class contains many struct’s representing common fixed-format vertex types. Basically, different vertex types that you’d commonly need to create using FVF codes in C++ are already defined by Managed DirectX.

There’s also no need to create a vertex buffer object like you would in native DirectX. You can use the original array of vertices and pass them directly into drawing routines. However, MDX still has the ability to work with vertex buffer objects if you need them.

These two major differences result in very simple drawing code. Drop the following code snippet into OnPaint() and you’re good to go:

CustomVertex.TransformedColored[] vertexes = new CustomVertex.TransformedColored[3];

 

vertexes[0].Position = new Vector4(50, 50, 0, 1.0f);
vertexes[0].Color = System.Drawing.Color.FromArgb(0, 255, 0).ToArgb();

vertexes[1].Position = new Vector4(250, 50, 0, 1.0f);
vertexes[1].Color = System.Drawing.Color.FromArgb(0, 0, 255).ToArgb();

vertexes[2].Position = new Vector4(50, 250, 0, 1.0f);
vertexes[2].Color = System.Drawing.Color.FromArgb(255, 0, 0).ToArgb();

 

m_device.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 0, 0).ToArgb(), 1.0f, 0);
m_device.BeginScene();        
m_device.VertexFormat = CustomVertex.TransformedColored.Format;
m_device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, vertexes);
m_device.EndScene();
m_device.Present();

The first line creates the vertex array while the next six lines fill in the information at each vertex (position and color). Then Clear() is called to erase the background. BeginScene() starts the 3D drawing code just like in C++. We tell DirectX about the vertex type being used by setting VertexFormat. This must match the vertex array element type. DrawUserPrimitives() takes the vertex array and renders the triangle. Finally we end the scene with EndScene() and show everything on the screen with Present(). What could be easier?

Full Listing

Since using .NET makes the text of this application so small, I’m going to list all the code here for your convenience. 🙂

using System;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

 

namespace MDX_Tutorial2
{
   class MainClass:Form
   {
      private Device m_device = null;

 

      MainClass()
      {
         this.Text = “Managed DirectX Tutorial 2”;
      }

 

      void InitGraphics()
      {
         PresentParameters present_params = new PresentParameters();

         present_params.Windowed = true;
         present_params.SwapEffect = SwapEffect.Discard;

 

         m_device = new Device(0, DeviceType.Hardware, this,
                               CreateFlags.SoftwareVertexProcessing, present_params);
      }

 

      protected override void OnPaint(PaintEventArgs e)
      {
         CustomVertex.TransformedColored[] vertexes = new CustomVertex.TransformedColored[3];

 

         vertexes[0].Position = new Vector4(50, 50, 0, 1.0f);
         vertexes[0].Color = System.Drawing.Color.FromArgb(0, 255, 0).ToArgb();
         vertexes[1].Position = new Vector4(250, 50, 0, 1.0f);
         vertexes[1].Color = System.Drawing.Color.FromArgb(0, 0, 255).ToArgb();
         vertexes[2].Position = new Vector4(50, 250, 0, 1.0f);
         vertexes[2].Color = System.Drawing.Color.FromArgb(255, 0, 0).ToArgb();

 

         m_device.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 0, 0).ToArgb(), 1.0f, 0);
         m_device.BeginScene();        
         m_device.VertexFormat = CustomVertex.TransformedColored.Format;
         m_device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, vertexes);
         m_device.EndScene();
         m_device.Present();
      }

 

      static void Main()
      {
         MainClass MainForm = new MainClass();
         MainForm.InitGraphics();
         Application.Run(MainForm);
      }
   }
}

Program Output

Now for the fun part. What does running the program look like? Here’s a screenshot:

managed_directx_tutorial_2_ouput

Look familiar? If it does, good. We set out to create the C# .NET equivalent of the C++ tutorial and did exactly that. If it looked different from last time, then we’d have a problem.

Conclusion

I hope you’ve gained insight into the differences and similarities of native DirectX versus Managed DirectX. If you have any questions or comments, please don’t hesitate to email me or leave a comment on this post.

Thanks for reading! 😉

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in .NET Graphics, 3D Graphics, DirectX | 8 Comments »

DirectX 9 C++ Graphics Tutorial 2: Drawing a Triangle

Posted by gregd1024 on February 28, 2008

In this tutorial we’re going to look at how to draw a stationary triangle using DirectX 9 and C++. We’ll be building off of the concepts taught in the first tutorial (DirectX 9 C++ Graphics Tutorial 1: Getting Started). Most of the code will be the same since it involves setting up a form and initializing DirectX.

Requirements

The tutorial sample code was built and tested with Visual Studio Express 2008. However, using the code “as-is” in Visual Studio 2005 should work too.

You’ll need the DirectX 9 SDK for compiling the sample code. Use this link for downloading: click here.

Tutorial Source and Project Files

Download the project files, binaries, and source with this link:

Getting Started

I’m not going to cover the Win32 initialization code or how to initialize DirectX in this tutorial. For that, see the previous tutorial: DirectX 9 C++ Graphics Tutorial 1: Getting Started.

The only difference between this tutorial’s code versus the last version is how we handle WM_PAINT messages. Previously, we simply cleared the color buffer and displayed it on the screen. This time we’re going to define three triangle vertices, create a vertex buffer, send that vertex buffer to DirectX, and finally tell DirectX to render the triangle.

Defining the Vertices

In DirectX there is no pre-defined vertex type or object. You have to make your own struct (or class) and then tell DirectX about its format via FVF (Flexible Vertex Format) codes. FVF codes are a set of constants that describe the contents and size of a vertex structure. For example, the constant D3DFVF_XYZ describes your structure as having three float variables representing an untransformed vertex; the constant D3DFVF_DIFFUSE describes a single DWORD value representing a diffuse color component in ARGB order. You can (and often will) combine a set of FVF code together. For instance, “D3DFVF_XYZ|D3DFVF_DIFFUSE” means your structure has three float variables followed by one DWORD variable. The correlation between constant and vertex layout is clearly defined in the DirectX SDK documentation.

In our sample program we used the following structure:

struct D3DVERTEX
{
   float x, y, z, rhw;
   DWORD color;
};

The (x, y, z, rhw) combination describe the transformed position of a vertex. The “color” member describes its diffuse color in the format of ARGB (Alpha, Red, Green, Blue). To describe this structure we use the following FVF code:

  • “D3DFVF_XYZRHW|D3DFVF_DIFFUSE” – transformed position with color info.

Let’s look at the code for handling the WM_PAINT event where it actually creates the vertices:

case WM_PAINT:

  // setup vertex information

  struct D3DVERTEX {float x, y, z, rhw; DWORD color;} vertices[3];

 

  vertices[0].x = 50;
  vertices[0].y = 50;
  vertices[0].z = 0;
  vertices[0].rhw = 1.0f;
  vertices[0].color = 0x00ff00;

 

  vertices[1].x = 250;
  vertices[1].y = 50;
  vertices[1].z = 0;
  vertices[1].rhw = 1.0f;
  vertices[1].color = 0x0000ff;

 

  vertices[2].x = 50;
  vertices[2].y = 250;
  vertices[2].z = 0;
  vertices[2].rhw = 1.0f;
  vertices[2].color = 0xff0000;

In this code we really just define a vertex array (“vertices[3]”) and fill in the values for a triangle.

Creating the Vertex Buffer

Next, we tell DirectX about our vertex data by creating a vertex buffer object. Here’s the code to do it (this code comes directly after the code in the last section):

LPDIRECT3DVERTEXBUFFER9 pVertexObject = NULL;
void *pVertexBuffer = NULL;

 

if(FAILED(g_pDirect3D_Device->CreateVertexBuffer(3*sizeof(D3DVERTEX), 0,
          D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVertexObject, NULL)))
   return(0);

if(FAILED(pVertexObject->Lock(0, 3*sizeof(D3DVERTEX), &pVertexBuffer, 0)))
   return(0);

memcpy(pVertexBuffer, vertices, 3*sizeof(D3DVERTEX));
pVertexObject->Unlock();

The first two lines just declare the pointers we’re going to use. The next line calls a DirectX function called CreateVertexBuffer(). This function allocates a vertex buffer object which we’ll use for all buffer operations.

CreateVertexBuffer() takes six parameters. The first parameter tells DirectX the required size of the vertex buffer (in bytes). The second parameter specifies how the vertex buffer will be used – “0” being the default. The third parameter tells DirectX about the memory layout of each vertex (the FVF format). The fourth parameter says that you don’t care where memory is allocated. The fifth parameter is the address of a pointer to be filled with the vertex buffer object location. Lastly, the sixth parameter specifies a shared handle (don’t worry about this).

Now we use our newly created vertex buffer object and call its Lock() method. This call gives us a memory buffer (pointed to by pVertexBuffer) that we must copy our vertex data into. I know this seems strange – we already created our own vertex array, filled it with data, now we have to copy it somewhere else? Don’t ask why, this is just how DirectX works. The next line, with the call to memcpy(), does this copying process.

Finally, when we’re done with the copy, we have to tell DirectX that the data is ready to go. This is done via the Unlock() method of the vertex buffer object.

Rendering the Vertex Buffer

Now we’re ready to actually draw the scene! Check out the following code (again, this code comes directly after the last line of the previous section):

// clear background to black
g_pDirect3D_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

 

// render the actual scene
if(SUCCEEDED(g_pDirect3D_Device->BeginScene()))
{
   g_pDirect3D_Device->SetStreamSource(0, pVertexObject, 0, sizeof(D3DVERTEX));
   g_pDirect3D_Device->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE);
   g_pDirect3D_Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
   g_pDirect3D_Device->EndScene();
}

 

g_pDirect3D_Device->Present(NULL, NULL, NULL, NULL);
pVertexObject->Release();

 

ValidateRect(hwnd, NULL);

First, we clear the background to black by calling Clear(). You might recognize this from the previous tutorial (it’s the exact same call, but the color is different).

Next we call BeginScene() – every 3D frame in DirectX begins with this call.

The next two lines set the stream source and vertex format. SetStreamSource() tells DirectX to take the vertex information from our vertex buffer object. The first parameter specifies the stream number and the second is a pointer to the vertex buffer object itself. The third parameter says there is no offset from the beginning of the stream to the vertex data (if there was, this value would be the number of bytes in between). Finally, the fourth parameter is the size, in bytes, of each vertex. SetFVF() sets the FVF code that describes our vertex format (see “D3DFVF” in the DirectX SDK documentation for all the possible code combinations and their corresponding formats).

Now we’re at DrawPrimitive(). This tells DirectX to actually draw something. The first parameter specifies what to draw – in our case, a list of triangles (actually, just one triangle since our list only contains one element). The second parameter says start from vertex zero (in some cases you may want to start from somewhere other than the beginning). And the last parameter tells DirectX how many primitives to draw (for us, just one triangle).

Once all the drawing code is executed, we must call EndScene(). All 3D scenes in DirectX end with this call.

Finally we call Present() to display everything on the screen and call Release() on our vertex buffer object (since it’s no longer needed).

ValidateRect() is from the first tutorial. It tells Windows that we’ve handled all of the drawing in this window and Win32 doesn’t need to do any further processing.

Program Output

All this code generates the following output:

directx_tutorial_2_cpp_output

Notice how the colors of each vertex are “smoothed” over the face of the triangle. This is the default behavior of DirectX (and even OpenGL). Since colors are only defined for the vertices and not the face of the polygon, DirectX interpolates the color in between. This interpolation gives you the gradient effect.

Conclusion

Now that’s definitely better than the last tutorial of simply drawing a solid color on a form. However, the triangle is technically still 2D. If it were a 3D triangle we could adjust the z coordinate at each vertex, giving them different depths, and watch the triangle get drawn in perspective. Currently that’s not possible with this code. In order to turn it into a “real” 3D triangle, we need a camera position, a transformation matrix, and a couple other components. We’ll be discussing all this in the next DirectX C++ tutorial.

However, for the very next post, I’ll go over the Managed DirectX version of this same stuff using C#.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in 3D Graphics, C++ Graphics, DirectX | 35 Comments »

Managed DirectX C# Graphics Tutorial 1: Getting Started

Posted by gregd1024 on February 26, 2008

I’m going to demonstrate the same principles as my last post (DirectX 9 C++ Graphics Tutorial 1), how to fill a form with a solid color – except this time it’ll be in .NET using C# and Managed DirectX (MDX 1.1). MDX 1.1 is meant to expose most of the functionality as native DirectX 9.

Requirements

Everything in this tutorial is based on Visual Studio 2008 Express.

You’ll also need the DirectX SDK. You can download it from Microsoft’s site with this link. Note that this is the June 2007 release. I purposely didn’t link to the latest (November 2007 at the time of this writing) because Microsoft has removed the Managed DirectX samples and documentation from that release. If you read my last post, then you know that Microsoft has moved MDX’s functionality into the XNA framework, but threw out all features which were not simultaneously supported by both Xbox and Windows.

Tutorial Source and Project Files

To get the finished result of what this tutorial teaches – project files, binaries, and source – use this link:

Download C# Sources and Binaries [~10k]

Step 1: Create and Setup Your Project

We’re going to start from absolute scratch – that means no wizards and no auto-generated code. So start out by creating an empty C# project. You can name it whatever you like and place it in any directory.

Now add the following references:

  • Microsoft.DirectX
  • Microsoft.DirectX.Direct3D
  • System
  • System.Drawing
  • System.Windows.Forms

Go into your project settings and change the output type from “Console Application” to “Windows Application.”

managed_directx_tutorial_1_project_settings

Add one source file called “main.cs” (actually, you can use a different name but I’m going to refer to this file as “main.cs” throughout the tutorial).

Step 2: Create the Form

Inside “main.cs” you’re going to add the following “using” statements:

using System;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

To make the form, copy the following code snippet and paste it directly underneath the “using” statements:

namespace MDX_Tutorial1
{
   class MainClass:Form
   {
      MainClass()
      {
         this.Text = “Managed DirectX Tutorial 1”;
      }

 

      static void Main()
      {
         MainClass MainForm = new MainClass();
         Application.Run(MainForm);
      }
   }
}

Step 3: Create the Device Object

Just like the previous post’s C++ example, you’ll have to create a device object. Since we’ll be accessing this object in multiple functions, it must be a class member variable. Add the following declaration inside “MainClass”:

private Device m_device = null;

Also add a method called “InitGraphics()” – this is where we’ll create and initialize the device object. The initialization code is only a few lines:

void InitGraphics()
{
   PresentParameters present_params = new PresentParameters();
  

   present_params.Windowed = true;
   present_params.SwapEffect = SwapEffect.Discard;
  

   m_device = new Device(0, DeviceType.Hardware, this,

                         CreateFlags.SoftwareVertexProcessing, present_params);
}

First, we create the “PresentParameters” object. This object describes how DirectX should behave in our application. We want the application to be a window and we’ll let the device handle the back buffer (“Windowed” and “SwapEffect”). Next, we actually create the device. There are several overloads for the constructor of “Device” but the one we’re interested in takes five arguments as described below:

  • Parameter 1: “int adapter”
  • Parameter 2: “DeviceType deviceType”
  • Parameter 3: “Control renderWindow”
  • Parameter 4: “CreateFlags behaviorFlags”
  • Parameter 5: “params PresentParameters[] presentationParameters”

For the adapter we used “0” since this designates the default. We chose hardware rendering as opposed to software emulation. Our main application class is also our render target form (hence the “this” keyword). We wanted software vertex processing instead of hardware calculated transforms. Lastly, we passed in our “present_params” object as the fifth parameter.

Now we must call “InitGraphics()” from somewhere. Add the following line in “Main()”:

static void Main()
{
   MainClass MainForm = new MainClass();
   MainForm.InitGraphics(); // <— ADD THIS LINE
   Application.Run(MainForm);
}

Step 4: Draw on the Form

In order to draw on the form we must override the “OnPaint()” function. In this function we use the “m_device” member to call “Clear()” and “Present()” like this:

protected override void OnPaint(PaintEventArgs e)
{
   m_device.Clear(ClearFlags.Target,
                  System.Drawing.Color.FromArgb(0, 0, 255).ToArgb(), 1.0f, 0);
   m_device.Present();
}

The “Clear()” function fills one or more buffers with a single value. The first parameter specifies which buffer, or series of buffers, to fill. It can be the rendering target buffer (Target), depth buffer (ZBuffer), or stencil buffer (Stencil). You can specify more than one by combining their values via a bitwise “OR” operation. The second parameter specifies what color to use in clearing the render target surface. In our case we chose blue. The third parameter specifies what value to use for clearing the depth buffer. Lastly, the fourth parameter specifies what value to use for clearing the stencil buffer.

The “Present()” function displays everything to the screen. It is equivalent to calling the C++ version with four NULL parameters.

Run the Program!

Our first managed DirectX program is done! Now compile and run the program. You should see output similar to this:

managed_directx_tutorial_1_output

Conclusion

While this program is rather boring, in the next tutorial we’ll cover how to draw a 2D triangle with different colored vertices.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in .NET Graphics, C# Programming, DirectX | 35 Comments »

DirectX 9 C++ Graphics Tutorial 1: Getting Started

Posted by gregd1024 on February 20, 2008

In this post I’m going to cover writing the most basic DirectX 9 application in native C++. You’ll see how to paint a solid color on a form as well as the minimum amount of code all DirectX 9 applications must have. Note: when I refer to DirectX, unless otherwise specified, I’m strictly referring to the graphics portion of it – Direct3D.

This post is going to be one of a series. I’m going to cover writing simple DirectX 9 applications in C++ and I’ll also cover how to write those same applications with Managed DirectX  (MDX 1.1) in C#. These posts will alternate – one in C++, one in C#, back to C++, and so on. After this, I’ll eventually cover some DirectX 10 stuff in C++ (which can be vastly different from DirectX 9 in certain areas). There is no MDX equivalent of the C++ DX10 API – the XNA Framework is meant to replace MDX since Microsoft dropped support for the MDX 2.0 Beta project. However, I’m not sure if XNA is mature enough yet to be able to do all of things originally included in MDX 2.0. I’ll explore that issue in a future post.

Requirements

The steps and screenshots in this tutorial are based on Visual Studio 2008 Express. However, everything was also tested in Visual Studio 2005 Professional.

You’ll need the DirectX SDK to run the sample programs. You can download it from Microsoft’s site with this link. Note, however, that their latest release (which is November 2007 at the time of this writing) took out the MDX 1.1 samples and documentation. If you still want those, download the August 2007 release or earlier (I use the June release).

Tutorial Source and Project Files

To get the finished result of what this tutorial teaches – project files, binaries, and source – use this link:

If you follow this tutorial’s instructions exactly as specified, you should end up with the same code and output. However, copying and pasting from a web page to Visual Studio sometimes results in double spaces between lines where it wasn’t intended. In almost all cases C++ doesn’t care and your code will still compile perfectly fine (a multi-line macro is one of those rare exceptions).

Setting Up the Project

Start out by performing the following steps:

Create an empty project and add one source file called “main.cpp” (you can actually name it anything you want, but I’m going to refer to this file as “main.cpp” throughout the text).

Go into the Project Properties dialog and add the following include directory, “$(DXSDK_DIR)\Include”:

directx_tutorial_1_cpp_include_dirs

Add the following preprocessor directives, “WIN32;_DEBUG;_WINDOWS”:

directx_tutorial_1_cpp_preprocessor_directives

Add the following library dependency, “d3d9.lib”:

directx_tutorial_1_cpp_lib_dependencies

All the other defaults are fine. Save these settings and move on to the next step.

Creating the Form

Drop the following code snippet into your main.cpp file. This is the smallest amount of code that all C++ Windows form applications must have:

#include <windows.h>

 

LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine,

                   int nShow)
{
   MSG msg;


   WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_VREDRAW|CS_HREDRAW|CS_OWNDC,
                    WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1),
                    NULL, “DX9_TUTORIAL1_CLASS”, NULL};

   RegisterClassEx(&wc);

   HWND hMainWnd = CreateWindow(“DX9_TUTORIAL1_CLASS”,

                                “DirectX 9 Bare Bones Tutorial 1”,
                                WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,

                                NULL, NULL, hInstance, NULL);
   ShowWindow(hMainWnd, nShow);
   UpdateWindow(hMainWnd);

   while(GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
  

   return(0);
}

 

LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
      case WM_DESTROY:
         PostQuitMessage(0);
         return(0);
   }

   return(DefWindowProc(hwnd, msg, wParam, lParam));
}

Since the purpose of this tutorial is to go over DirectX and not basic Win32 programming, I’m going to keep the following description of the code brief. There are many Win32 tutorials on the internet that explain in extreme detail the low-level aspects of Win32.

First we must define a WinMain() function and include <windows.h>. The windows.h header file includes most of the definitions needed to access the Win32 API. In order to run any Win32 program, the following three steps must be performed:

  1. Create a windows class.
  2. Create a window object.
  3. Show the window.

In addition to these steps, all Windows programs must have something called a “message pump.” This is the mechanism in which the Win32 subsystem sends your application events about what is happening on the outside (i.e., what the user is doing in your app, global events of the operating system – such as starting to reboot, etc.). Your application must either respond to these events or must pass them to the default Win32 event handler.

For the first step, create a Windows class – this doesn’t have anything to do with a C++ class. It is a Win32 specific term that tells Windows about your program. It needs some minimal information in order to setup your application to run in the Windows environment. It needs to know:

  1. How GDI should handle your application’s main window.
  2. A function pointer to your program’s message handler.
  3. The instance handle of your program.
  4. A background brush (this represents the background color, but technically it’s not required – your window will appear transparent if omitted).
  5. Class string (a unique string that identifies your Win32 class object to the rest of Windows).

The WNDCLASSEX structure is used to hold this information. While there are more than five members in WNDCLASSEX, the code above specifies NULL or “0” for the properties in which Windows will take the default. The RegisterClassEx() function is used to send this information to the Win32 subsystem.

The next step is to create the actual window object. This object turns into the application’s main form. The CreateWindow() function, like its name implies, is used to create a window object. This function needs at least the following information:

  1. The same class string used when calling RegisterClassEx().
  2. The title of the form.
  3. The style of the form (dialog, tool window, etc.).
  4. The (x, y) position of the form and its size.
  5. The instance handle of your program (this should match the instance handle used when creating the class object).

As long as no error is generated, the CreateWindow() function returns a handle to your window. NULL will be returned if an error occurred. The next step is take that window handle and call ShowWindow(). The first parameter specifies the window handle and the second parameter represents how the window is to be shown by default. We used the “nShow” variable as the second parameter – the forth argument to WinMain(). This allows Windows to tell us how the window should be shown based on how the application was launched. The next line calls UpdateWindow(), which is technically not required by a program this simple. It allows any post-updates to occur after the window is first drawn.

We now get to the while() loop at the end of the main function. This loop is part of the “message pump” discussed earlier. If this loop wasn’t there your application would simply start and stop right away. There needs to be something that keeps your program running until it’s time to close. The “message pump” loop does exactly this. It continually checks for events from the Win32 subsystem by calling GetMessage() and subsequently passes them down to TranslateMessage() and DispatchMessage(). These two functions eventually route the events to WndProc() – your callback function specified in the windows class object. This function allows your program to execute whatever code is necessary in response to these events. If your application doesn’t need to respond to an event, it should pass it down to the default Win32 handler by calling DefWindowProc().

Creating the DirectX Object and Device

Now we must create two objects: the main DirectX object that allows us to interface with the API and the “device” object which allows interaction with the 3D hardware. Begin doing this, you’ll need to include the Direct3D header file and declare a couple global pointers to the DirectX object and device object:

#include <d3d9.h>

 

// globals
LPDIRECT3D9       g_pDirect3D = NULL;
LPDIRECT3DDEVICE9 g_pDirect3D_Device = NULL;

In order to create the DirectX object and device object, we add the following code directly under the call to CreateWindow() right above ShowWindow():

g_pDirect3D = Direct3DCreate9(D3D_SDK_VERSION);

D3DPRESENT_PARAMETERS PresentParams;
memset(&PresentParams, 0, sizeof(D3DPRESENT_PARAMETERS));

PresentParams.Windowed = TRUE;
PresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;

g_pDirect3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hMainWnd,

                          D3DCREATE_SOFTWARE_VERTEXPROCESSING, &PresentParams,

                          &g_pDirect3D_Device);

Let’s go through this line by line. First we create the DirectX object by calling Direct3DCreate9(). The first parameter, D3D_SDK_VERSION, is a special number that ensures an application was built against the correct header files. If the number doesn’t match, the call will fail.

Next we declare and fill in a D3DPRESENT_PARAMETERS structure. This object holds properties about how an application should behave. In our sample, we want it to run in windowed mode and we want the video hardware to handle the back buffer instead of handling it ourselves (the full meaning of D3DSWAPEFFECT_DISCARD is beyond the scope of this tutorial, but basically it tells the device driver to select the most efficient way of handling swap chains).

We then use the main DirectX object to call CreateDevice(). This function does the work of allocating the device object and associating that object with the 3D hardware (or software – depending on what parameters are specified). The first parameter, D3DADAPTER_DEFAULT, tells DirectX to take whatever graphics card is the default (typically the one and only piece of active video hardware connected to your motherboard). The second parameter, D3DDEVTYPE_HAL, tells DirectX to use 3D hardware, instead of a software emulator, for rendering. A software emulator becomes useful only for low-level debugging – don’t worry about it for now. The third parameter specifies the window handle that will receive the rendered output. D3DCREATE_SOFTWARE_VERTEXPROCESSING tells DirectX to use software for transformation calculations instead of hardware HTL (Hardware Transform & Lighting). If you had specified hardware transformation calculations instead, and the video card didn’t support HTL, then this function would fail. All new cards on the market today support HTL, and most have for quite a while. So unless your card is really old you can select hardware processing without any worries – just replace the existing constant with D3DCREATE_HARDWARE_VERTEXPROCESSING. The fifth parameter is a pointer to the D3DPRESENT_PARAMETERS object that we filled in right above the call. Lastly, the sixth parameter holds a pointer to the device object that will be created and returned by the function.

Now that the device object is created, we can call its functions to control the video card! The next section will explain exactly how to do this.

Draw on the Window

In this section we’ll see where the magic happens. While it’s not much – drawing a solid color in the window – you’ll see how to do more advanced stuff in the next few tutorials.

Before we begin, we must first handle the WM_PAINT event inside our WndProc() message handler function. The WM_PAINT event is fired whenever the interior of a window needs its contents redrawn. We capture the event by enumerating the WM_PAINT constant inside WndProc’s switch statement:

switch(msg)
{
   case WM_DESTROY:
      PostQuitMessage(0);
      return(0);
   case WM_PAINT: // <— ADD THIS BLOCK
      // drawing code goes here…
      ValidateRect(hwnd, NULL);
      return(0);
}

ValidateRect() is called in order to tell Win32 that we’ve handled all of the rendering ourselves.

Now for the fun part – filling in the drawing code. Add the following two lines directly below the “case WM_PAINT” statement (where it says, “drawing code goes here…”):

g_pDirect3D_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255),

                          1.0f, 0);
g_pDirect3D_Device->Present(NULL, NULL, NULL, NULL);

The first call to Clear() tells DirectX to fill a specific rectangle, or set of rectangles, with a certain color. The first two parameters represent the number of rectangles and the address of where those rectangles are stored. If “0” and “NULL” are specified instead, DirectX will fill the entire rendering target. The third parameter, D3DCLEAR_TARGET, means we want to clear the actual pixel buffer as opposed to the depth buffer or stencil buffer. This parameter could also be used to clear a combination of those buffers at the same time. The last three parameters tell DirectX what values to use for clearing the pixel buffer, depth buffer, and stencil buffer. Since we’re not clearing the depth buffer or stencil buffer, the values for the last two parameters have no effect.

Once everything has been rendered to the back buffer you display it on the screen by calling Present(). Parameters one and two represent the source and destination rectangles, respectively. Since we want the entire back buffer to get copied to the entire screen, we pass in NULL. The third parameter specifies which window to use for the display. When this parameter is NULL, Present() looks at what was originally set when creating the D3DPRESENT_PARAMETERS object (specifically the hDeviceWindow member). Since we didn’t specify anything in the beginning of our program, the memset() call would have set this member to NULL. When this happens, and windowed mode is set, Present() takes the focus window as its destination.

Shutting Down

We allocated two DirectX objects in our program – the main interface object and the device object. These objects must eventually be freed. Since we aren’t doing anything fancy that requires destruction and reallocation of these objects, we can simply put the clean up code right above the “return” statement in WinMain():

g_pDirect3D_Device->Release();
g_pDirect3D->Release();

We call Release() because these objects were given to us by DirectX via COM as opposed to being allocated with the “new” keyword or created with some memory allocation function. Therefore we must delete them through COM’s Release() function.

Run the Program!

You can now compile and run the program. Here’s what the output looks like:

 directx_tutorial_1_cpp_output

OK, it’s not much at this point, but in the next few tutorials we’ll get into more exciting things.

Conclusion

OK, I admit, the final output of this tutorial wasn’t too exciting, but it only strived to cover the absolute minimum – nothing more, nothing less. All 3D DirectX applications start off this way. If someone made a DirectX project template for Visual Studio, the wizard would more than likely generate very similar code to what you see here.

In the next tutorial, we’ll see how to do the same thing in managed DirectX (MDX 1.1).

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in C++ Graphics, DirectX | 64 Comments »

Can I Use Typedef’s in C#?

Posted by gregd1024 on February 15, 2008

When moving from C++ to C#, the following question often arises: does C# support typedef’s? Unfortunately, no. However, you can get pretty close. Consider this code:

using GLenum = System.UInt32;

The “using GLenum” statement makes the symbol “GLenum” equivalent to a “System.UInt32” name. Now you can use “GLenum” to declare identifiers instead of “UInt32.” In this way, it’s acting exactly like a C or C++ typedef. What’s actually happening is that you’re creating a C# alias – a mechanism which allows the renaming of a class, struct, or namespace. While this may seem pretty flexible, consider what happens when you try the following:

using GLfloat = float; // ERROR!

This line results in a compile error because “float” is a keyword. Why weren’t C# aliases built to support this as well? When you consider the original purpose of aliases it kind of makes sense. They were meant to save the programmer from lots of typing by providing a mechanism to shorten long qualifiers. We all know some namespaces and classes in C# are much longer than what you would typically see in a C++ program, so providing renaming support was practically a necessity. But do you really think a lot of programmers are going to shorten “float” or “int”? So when I said you can “get pretty close” to a C++ typedef, I was specifically referring to limitations such as this.

Another big limitation is that aliases are limited in scope. Only the namespace in which they’re declared can see them and their scope cannot cross multiple source files. The last part is severely limiting – it forces you to include the same declaration in every file. At least it’s better than nothing.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in C# Programming, Tips and Tricks | 8 Comments »

How to Make a C# Application Without the Wizard

Posted by gregd1024 on February 14, 2008

Most C++ programmers know how to start coding from an empty project without the wizard’s assistance. But how many tutorials show a way to do this in C#? Questions arise like what references you need, what should the default project settings be, etc.

In this post I’m going to create a Windows Form application starting from scratch (just an empty project). It’s actually quite easy if you’re making a DLL, Console application, or Forms application. Plus, you can be certain that your application doesn’t contain any useless references or dependencies.

Note: this tutorial is applicable to Visual Studio 2008.

Step 1 – Create an Empty Project

Start Visual Studio and select “File->New Project.” When the “New Project” dialog appears, select the “Empty Project” template and name it whatever you like. Click OK.

c_sharp_app_from_scratch_new_project_dialog

Step 2 – Modify Project Settings

At this point you’ll have an empty project loaded in Solution Explorer without any source files or references listed. Now, go to the “Project” menu and select “Properties.” Under the “Application” tab, click the “Output type” combo box and change it from “Console Application” to “Windows Application.” This makes the project into a regular Windows Form app.

c_sharp_app_from_scratch_project_settings

Save this configuration (hit CTRL-S or click “File->Save”) and close the “Project Settings” window.

Step 3 – Add References

Now right click on the “References” folder in Solution Explorer and click “Add Reference” (you can also select the same option from the “Project” menu). Which references you’ll need will depend on what kind of project you’re building, but in our case we need “System,” “System.Windows.Forms,” and “System.Drawing.” They are located in the .NET tab of the “Add Reference” dialog:

c_sharp_app_from_scratch_system_reference

After you’ve selected all three references, click OK. Now your project should look like this in Solution Explorer:

 c_sharp_app_from_scratch_solution_explorer_references

FYI: for almost any project you’ll at least need the “System” reference, so I add this one right away no matter what kind of project I’m creating. If your project was extremely simple, had no dependencies, and you didn’t care about fully qualifying your types, then you could get away with not adding this reference.

Step 4 – Add a Source File

You’ll notice from the last screenshot that we don’t have any source files in our project. Well that’s easy to fix – click on “Add New Item” located under the “Project” menu. When the “Add New Item” dialog appears, select “Code File.” Name it whatever you like and click OK.

c_sharp_app_from_scratch_add_code_file

Step 5 – Add Code

We didn’t want to select the “Class” template because that would cause Visual Studio to create a class skeleton and add useless references to the project.  Selecting “Code File” ensures that nothing is done which isn’t completely necessary. It also gives us an entirely blank source file. Here is the minimum amount of code you’ll have to add for some meaningful output:

  1. Type the “using” directives for “System,” “System.Windows.Forms,” and “System.Drawing.”
  2. Type a namespace declaration.
  3. In that namespace type a class declaration deriving from “Form”.
  4. Type a “static void Main()” function into the class.
  5. Inside the Main() function type this line: “Application.Run(new MyClass());”

After following the steps above, your code should look similar to this:

using System;
using System.Windows.Forms;

using System.Drawing;

 

namespace MyProject
{
   class MyClass:Form
   {
      static void Main()
      {
         Application.Run(new MyClass());
      }
   }
}

You can actually compile and run the project in this state, but it will just produce a blank form and you won’t be able to add controls in the Designer. We’ll take care of this problem in the next step.

Step 6 – Add Controls to Form

You may be wondering how to associate your hand-written code with Visual Studio’s Designer. Believe it or not, there’s no need to do this. In fact, Visual Studio automatically detects whenever you derive a class from “System.Windows.Forms.Form” and links the associated source file to the Designer.

Now, right click on your source file in Solution Explorer and select “View Designer.” When the Designer comes up you’ll see an empty form. Drag a button control on it and switch back to code-view. You’ll notice that Visual Studio has added this function to your class:

private void InitializeComponent()
{
   this.button1 = new System.Windows.Forms.Button();
   this.SuspendLayout();
   //
   // button1
   //
   this.button1.Location = new System.Drawing.Point(104, 93);
   this.button1.Name = “button1”;
   this.button1.Size = new System.Drawing.Size(75, 23);
   this.button1.TabIndex = 0;
   this.button1.Text = “button1”;
   this.button1.UseVisualStyleBackColor = true;
   //
   // MyClass
   //
   this.ClientSize = new System.Drawing.Size(284, 264);
   this.Controls.Add(this.button1);
   this.Name = “MyClass”;
   this.ResumeLayout(false);

}

Visual Studio automatically keeps InitializeComponent() up-to-date. It contains all the code for creating and positioning the form’s controls. However, we still have to call the function. For this, we turn to the class’s constructor. Since we haven’t defined a constructor yet, we’ll do it now and add the call to InitializeComponent():

MyClass()
{
   InitializeComponent();
}

By now, your source code should look like this:

using System;
using System.Windows.Forms;
using System.Drawing;

 

namespace MyProject
{
   class MyClass:Form
   {
      private Button button1;
     

      MyClass()
      {
         InitializeComponent();
      }
     

      static void Main()
      {
         Application.Run(new MyClass());
      }

     

      private void InitializeComponent()
      {
         this.button1 = new System.Windows.Forms.Button();
         this.SuspendLayout();
         //
         // button1
         //
         this.button1.Location = new System.Drawing.Point(104, 93);
         this.button1.Name = “button1”;
         this.button1.Size = new System.Drawing.Size(75, 23);
         this.button1.TabIndex = 0;
         this.button1.Text = “button1”;
         this.button1.UseVisualStyleBackColor = true;
         //
         // MyClass
         //
         this.ClientSize = new System.Drawing.Size(284, 264);
         this.Controls.Add(this.button1);
         this.Name = “MyClass”;
         this.ResumeLayout(false);

      }
   }
}

Step 7 – Compiled and Run

Now try running the project! Hopefully you see something similar to my output:

c_sharp_app_from_scratch_output_form

Congratulations! You just created a Windows Forms application from scratch!

Optional Things

You may be thinking, “but what about the extra code the wizard adds in forms like Dispose() and the “components” member? Don’t I need that stuff too?” The short answer: it depends.

In certain situations you’ll need the “components” member, but it’s not necessary to define it. The Designer will add the declaration when it’s needed. Try placing a Notify Icon or Timer control and you’ll see what I mean. The “components” member is used for most of the non-visual controls available in the Designer. If you drag a control on a form and it gets added to the container bar (on the bottom of the design window) instead of making some visual appearance on the form itself, then there’s a good chance it’ll use this variable.

You’ll need the Dispose() function only if you must explicitly free resources that can’t otherwise be managed by the Garbage Collector. These resources might be unmanaged memory, file handles, database connections, or similar objects who’s destruction is non-deterministic. Given that our example program allocated objects only from the managed heap, having a Dispose() would be pointless.

If you find that your class will eventually need the “components” member and a Dispose() method, here’s the minimum amount of code you’ll need to get started:

// Special Designer variable
private System.ComponentModel.IContainer components = null;

 

// Used to explicitly free any resources not
// managed by the Garbage Collector
protected override void Dispose(bool disposing)
{
   if (disposing && (components != null))
   {
      components.Dispose();
   }
   base.Dispose(disposing);
}

Also, you may have noticed that when using the wizard to generate a project, it creates a separate form class instead of using the application’s main class as the container. This results in two extra source files being added to the solution. You can do this yourself by creating one source file giving it a “.cs” extension (such as “Form.cs”) and creating another file with the same name but giving it a “.Designer.cs” extension (such as “Form.Designer.cs”). Visual Studio will automatically associate these two files together and you’ll see “Form.Designer.cs” underneath “Form.cs” in Solution Explorer’s tree view:

c_sharp_app_from_scratch_manual_form_files

Conclusion

I hope that this post has given you insight into rolling your own Windows Forms project from scratch. While it will always be faster to use the wizard, at least now you don’t have to rely on it. You also know what parts of the wizard’s code can be deleted if your program doesn’t require it.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in C# Programming, General Programming, Tips and Tricks | 6 Comments »

Tip: How to Delete MRU Items from Visual Studio 2008

Posted by gregd1024 on February 11, 2008

How many times have you had a project (or solution) in Visual Studio’s MRU list that you didn’t want? Maybe it was a project you opened by mistake, or maybe it was something you only needed in rare circumstances. Whatever the reason, did you also try looking for an option in Visual Studio that would allow you to delete those MRU entries? I’ve been in the same situation and, unfortunately, I had no luck finding a way to delete a specific entry in the list. At least, not directly through Visual Studio.

In this post I’ll show you a hack that will allow specific entries to be deleted. It requires a few registry modifications and isn’t the cleanest solution, but at least it gets the job done. If you know of another way to do this directly through Visual Studio then, by all means, please leave a comment.

The first thing you’ll need to do is open one of the following registry keys:

  • Visual Studio Express for C++: HKEY_CURRENT_USER\Software\Microsoft\VCExpress\9.0\ProjectMRUList
  • Visual Studio Express for C#: HKEY_CURRENT_USER\Software\Microsoft\VCSExpress\9.0\ProjectMRUList
  • Visual Studio Professional: HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\ProjectMRUList

It will look something similar to this:

registry_MRU_list

Here you’ll see paths to each of your MRU projects. The entry which appears first in Visual Studio’s list is labeled “File1,” the second is “File2,” etc. Simply delete whichever entry holds the path to the project you don’t want. If you’re deleting the last entry, you can stop here. Otherwise, you’ll have to rename all successive entries such that the numbering is consecutive.

Once you’ve deleted the entry or entries representing your project(s), open a new instance of Visual Studio – notice how the MRU list just got shorter! 😉

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in Miscellaneous, Tips and Tricks | Leave a Comment »

How to Convert a Console App into a Windows App in C# – Part Two

Posted by gregd1024 on February 9, 2008

As I promised in yesterday’s post, I’m now going to show you an even easier way to convert a C# console application into a regular Windows application. Since the steps are nearly identical, I’m just going to focus on the key points instead of writing the same thing as before.

First, perform the following steps:

  • Create a regular C# console application or open one of your own.
  • Add the following two references to your project and the corresponding “using” statements on top of the main source file:

using System.Drawing

using System.Windows.Forms

  • Remove the “args” string array parameter from the Main() function.
  • Add “Application.EnableVisualStyles();” as the first call in the Main() function.
  • Change the “Output type” of your project from “Console Application” to “Windows Application.”

Now for the fun part (the part that’s different from my last post). Do the following:

  • Change the “Program” class to derive from “System.Windows.Forms.Form.”
  • Add “Application.Run(new Program());” as the last call in the Main() function.

At this point your “Program” class should look like this:

class Program:System.Windows.Forms.Form

{

   static void Main()

   {

      Application.EnableVisualStyles();

      Application.Run(new Program());

   }

}

We’re almost done. In fact, you can run the program now and you’ll see a blank form. But you won’t be able to place any controls on it. Well, actually that’s not true – you can place controls, but the form will still be blank at runtime. The last step is:

  • Add a constructor to the “Program” class and call “InitializeComponent()” inside it:

class Program:System.Windows.Forms.Form

{

   Program() // ADD THIS CONSTRUCTOR

   {

      InitializeComponent();

   }

 

   static void Main()

   {

      Application.EnableVisualStyles();

      Application.Run(new Program());

   }

}

“InitializeComponent()” will not be defined yet. However, the Form Designer will create this function as soon as any control is placed on the form or its layout is modified. You may have noticed that the icon next to “Program.cs” (in Solution Explorer) has changed to a form instead of the one representing a code file:

console_convert_2_icon_change 

This means you can double-click on it and Visual Studio will open the Form Designer instead of going to the source. Try this:

  1. Double click on Program.cs.
  2. Change the form’s size in the Designer.
  3. Go back to the source window of Program.cs.

Now you should see the “InitializeComponent()” function defined underneath “Main().” Your “Program” class will now look like this:

class Program:System.Windows.Forms.Form
{
   Program()
   {
      InitializeComponent();
   }
     
   static void Main()
   {
      Application.EnableVisualStyles();
      Application.Run(new Program());
   }

 

   private void InitializeComponent() // DESIGNER WILL ADD THIS FUNCTION
   {
      this.SuspendLayout();
      //
      // Program
      //
      this.ClientSize = new System.Drawing.Size(367, 188);
      this.Name = “Program”;
      this.ResumeLayout(false);

 

   }
}

You’re done! 🙂 Everything is contained in this one source file. The designer will keep on modifying “InitializeComponent()” whenever something changes in the form itself. It will also add the necessary member variables to the “Program” class whenever controls are placed on the form.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in C# Programming, General Programming, Tips and Tricks | 22 Comments »

How to Convert a Console App into a Windows App in C#

Posted by gregd1024 on February 8, 2008

In this post I’m going to demonstrate how you can easily take a simple C# console application and convert it into a regular Windows application. It will involve just a few code changes and one project settings change. There are actually two methods to do it – a method in which you end up with almost the same code the wizard normally generates, and a method in which your application class becomes the main form. I’m only going to demonstrate the first method in this post; I’ll cover the second method in my next post.

The instructions presented in this post are for Visual Studio 2008, but most should work in a similar way with previous versions. I’m also going to use Express for the programmers who don’t have full blown Visual Studio.

Step 1: Create a C# Console Application

For demonstration purposes I’m creating a C# console application from the wizard. Of course, if you already have a console app you want to convert, open your project instead.

console_app_conversion_new_project

Step #2: Add References

Add the following references to the project:

  1. “System.Drawing”
  2. “System.Windows.Forms”

console_convert_add_references

These references are not added by the console app generator wizard, so we must add them manually. Also, add the corresponding “using” statements to your application’s main source file (the file that contains the Main() function):

  1. “using System.Drawing”
  2. “using System.Windows.Forms”

Step Three: Modify the Main() Function

Perform the following steps to the Main() function:

  1. Remove the “args” parameter from Main().
  2. Add the following two lines:

static void Main()

{

   Application.EnableVisualStyles(); // <– ADD THIS LINE

  

   // your existing code goes here (if any)

 

   Application.Run(new Form1()); // <– ADD THIS LINE

}

You can optionally add “Application.SetCompatibleTextRenderingDefault(false);” right under the EnableVisualStyles() call, but it’s not required. I mention it because this is what the wizard adds when auto-generating a project, but its purpose is beyond me (if you know what this function does, please leave a comment).

Step Four: Add a New Form Resource to the Project

Add a new form to the project and keep the name as Form1.cs:

console_convert_add_form

Step Five: Modify Project Settings

To modify the project settings go to the Project menu and click on Properties. Under the first tab (Application), click the Output type drop-down box and change it from “Console” to “Windows Application.”

console_convert_project_settings

Step Six: Compile and Run!

We’re done! 🙂 Now try running the project. You should see a blank form without any console box:

console_convert_up_and_running

Conclusion

In my next post I’m going to show you an even simpler way of doing this very same thing. Instead of explicitly adding a Form resource to the project, we’ll just take the class which houses Main() and turn it into a form programmatically. You’ll even be able to use the Form Designer to add controls and modify layouts.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Posted in C# Programming, General Programming, Tips and Tricks | 17 Comments »