Points

Introduction

Defining our Vertices

Initializing Data

Input Handling

Lesson Downloads

Introduction

In this lesson we are going to plot points defined in Screen Space. We'll generate 2 different data sets and plot them. This lesson builds on a D3DApp class that is essentially the same as the one used in the Initializing Direct3D lesson. It's a little cleaner since I merged the functionality of the D3DWindow class into it as well.

Defining our Vertices

As mentioned in our 3D Primer, a point is defined by a single vertex. For our vertices we need a position, a RHW and to make things look more interesting, a colour. Here is our declaration of 2 arrays to hold our data.

   protected D3D.CustomVertex.TransformedColored [] m_sine_data;
   protected D3D.CustomVertex.TransformedColored [] m_random_data;
   Protected _sineData() As D3D.CustomVertex.TransformedColored
   Protected _randomData() As D3D.CustomVertex.TransformedColored

The TransformedColored vertex type has all the components we need and nothing else. If more advanced features were being used then it's likely that a pre-defined vertex type would not provide all that was needed. For now the pre-defined types will be fine.

Initializing Data

Since our points will be distributed on a flat plane, we won't really use the Z coordinate so we'll set it to 1.0. The rhw will also be set to 1.0 in each case. I won't go into a lot of detail about how I generate the data since it's not really important. Basically the first data set is a sine wave that's scaled and offset. The green channel of the points is determined by how close they are to the left edge, and the blue channel by their proximity to the right edge. The red channel is calculated from the magnitude (absolute value of the distance from the horizontal axis). Our second data set is just a bunch of random points with random colours. Here's the code to generate the second data set.

   Random rand = new Random();

   for (int count=0;count < width;count++) {

      m_random_data[count].X = rand.Next(width);
      m_random_data[count].Y = rand.Next(height);
      m_random_data[count].Z = 1.0f;
      m_random_data[count].Rhw = 1.0f;

      blue = rand.Next(200) + 55;
      green = rand.Next(200) + 55;
      red = rand.Next(200) + 55;

      colour = Color.FromArgb(red,green,blue);
      m_random_data[count].Color = colour.ToArgb();

   }
   Dim rand As Random = New Random

   For count As Integer = 0 To width - 1
     _randomData(count).X = rand.Next(width)
     _randomData(count).Y = rand.Next(height)
     _randomData(count).Z = 1.0F
     _randomData(count).Rhw = 1.0F

     blue = rand.Next(200) + 55
     green = rand.Next(200) + 55
     red = rand.Next(200) + 55

     colour = Color.FromArgb(red, green, blue)
     _randomData(count).Color = colour.ToArgb()
   Next count

The X and Y coordinate are set to random values, and the Z and RHW are both 1.0. Since we don't have a Z Buffer and Transformed Vertices have no perspective, our Z coordinate doesn't matter. Also, since we're plotting points, they'd be a single pixel regardless.

To create the colour 3 values are generated (red,blue,green) and they are combined into a Color using the FromArgb method. Since an Alpha (transparency) value wasn't provided it defaults to 255 (fully opaque). We haven't set up support for transparency in this application so it will be ignored anyway.

Input Handling

The input handling for this lesson is very simple. If the 'T' key is pressed the m_random_mode variable flips. This controls whether the sine or random data is shown. All other key events are passed through to D3DApp, which give us the ability to switch between Windowed and FullScreen as well as quit on Escape.

   protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e){

      switch(e.KeyCode) {
         case Keys.T:
            m_random_mode = !m_random_mode;
            break;
         default:
            base.OnKeyDown(e);
            break;
      }

   }
   Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs)
      Select Case e.KeyCode
         Case Keys.T
            _randomMode = Not _randomMode
         Case Else
            MyBase.OnKeyDown(e)
      End Select
   End Sub
Rendering Points

After all of the lead up and theory, we finally get to draw something. The render function is almost the same as it was in Initializing D3D. The first difference is that we have to set our FVF so the device knows how to interpret the point data we'll be sending it. This is done by setting the VertexFormat property to our FVF.

   m_device.VertexFormat = D3D.CustomVertex.TransformedColored.Format;
   _device.VertexFormat = D3D.CustomVertex.TransformedColored.Format

When you set the FVF it stays current until another one is set. So this could be moved to the init_scene function. If the device is lost we'd have to reset it as well. I kept it in the render function to keep the code together in a more logical, if not completely practical, way.

Next is the call that does the rendering. There are a variety of Draw calls, but the one we use here is DrawUserPrimitives. In a later lesson (Vertex Buffers) I'll show you a more efficient way of storing your data, but for now this works well. Here's the prototype for the function.

   public void DrawUserPrimitives (
      Microsoft.DirectX.Direct3D.PrimitiveType primitiveType,
      System.Int32 primitiveCount,
      System.Object vertexStreamZeroData
   )
   Public Sub DrawUserPrimitives(
      ByVal primitiveType As Microsoft.DirectX.Direct3D.PrimitiveType,
      ByVal primitiveCount As Integer,
      ByVal vertexStreamZeroData As Object
   )
primitiveType
The type of primitive we'll be rendering. In this case it's a point list (PrimitiveType.PointList).
primitiveCount
How many primitives to draw. Direct3D has no way of knowing how many primitives there are in our array. This can also be used to draw only a portion of the point list.
vertexStreamZeroData
This is where we pass the actual rendering data.

Here's our call to DrawUserPrimitives.

   m_device.DrawUserPrimitives(D3D.PrimitiveType.PointList, //The type of primitive we're rendering

                               ClientSize.Width,            //The number of primitives we're rendering

                               data);                       //The data to render

   _device.DrawUserPrimitives(D3D.PrimitiveType.PointList, ClientSize.Width, data)

We're drawing a Point List, and there are the same number of primitives as pixels across our display. We also pass in our data.

Now when you run the example program you'll see a random scattering of coloured dots. Hitting 't' will toggle you between that dot cloud and the plotting of a Sine Wave. Feel free to tinker with the data to plot different shapes. Remember that if you change the size of the data set you have to change the array allocation and the value passed to DrawUserPrimitives.

Lesson Downloads

Back