Vertex Buffers - Custom Vertex Format

Introduction

Microsoft's TransformedColored Vertex Format

The DrunkenHyena TransformedColored Vertex Format

Anatomy of a Custom Vertex Format

Wrapping Up

Lesson Downloads

Introduction

The main purpose of this lesson is to show you how to use a custom vertex format. The previous lesson on Vertex Buffers will be used as a base. Please note that the same vertex definitions can be used with DrawUserPrimitives, there is nothing about them that is specific to Vertex Buffers.

Microsoft's TransformedColored Vertex Format

In Microsoft.DirectX.Direct3D.CustomVertex there are a number of predefined vertex types. As nice as it is to have them there are always reasons want to create a custom format. It may be that none of the predefined ones fits your needs, it may be for educational reasons, or to improve the usability. For me the latter was the main motivation in creating my own format.

Usability is one of the prime marketing points for .Net. Managed DirectX is obviously intended to offer DirectX in a nice .Net flavour. In my opinion they missed on a lot of counts, and this article will address 1 of these. That issue is that the colour passed to the TransformedColored constructor is a signed integer.

A very common idiom in Direct3D programming is giving colour values in Hex encoded like so 0xAARRGGBB. It's very handy and easy to read once you get used to it. Unfortunately these values are unsigned so you cannot pass them into the constructor. The solution given in the Microsoft samples is to mark these sections of code as "unsafe". In a language intended to be "safe" it seems ridiculous to make a common bit of code require an unsafe tag. Another work around (showed in the previous lesson) is to construct a colour then convert it to an int, but that is far from elegant.

One benefit of of taking an int is that you can pass in the members of the System.Drawing.Color namespace. This is handy, but it still requires that you call the ToArgb function to convert that colour to one the TransformedColored constructor can consume. These issues can be easily fixed though and since there has been a lot of demand for information on creating custom formats I thought it would be a good idea to kill 2 birds with 1 stone.

The DrunkenHyena TransformedColored Vertex Format

This format is almost identical to Microsoft's in its use. The main difference is that it does not take an int for its colour. There are 2 different formats it does take: unsigned int, and System.Drawing.Color. This way you can directly pass in Color.White or encode it as 0xFFFFFFFF and it works. This does require that the constructor do some unsafe casting, but in my opinion that is where it should be done, in one isolated place not strewn throughout your code. Here is an example of code using this new class.

   list[0] = new dhVertex.TransformedColored( 75.0f,350.0f,1.0f,1.0f,Color.White);
   list[1] = new dhVertex.TransformedColored(  0.0f,225.0f,1.0f,1.0f,Color.Red);
   list[2] = new dhVertex.TransformedColored( 50.0f,215.0f,1.0f,1.0f,0xFFF7F700);
   list(0) = New dhVertex.TransformedColored( 75.0F, 350.0F, 1.0F, 1.0F, Color.White)
   list(1) = New dhVertex.TransformedColored( 0.0F, 225.0F, 1.0F, 1.0F, Color.Red)
   list(2) = New dhVertex.TransformedColored( 50.0F, 215.0F, 1.0F, 1.0F, &HFFF7F700)

Isn't that much cleaner? If there is demand for it I may create similar versions of the other common formats.

Anatomy of a Custom Vertex Format

Creating a Vertex Format similar to Microsoft's is pretty easy. The first step is determining what your format needs to contain. Since this will be a replacement for TransformedColored it's obvious that a transformed position and a colour will be needed. For the position we will store it as a 4 floats and the colour as an UInt32. Additionally it will have its Format and StrideSize. In setting the StrideSize a helper function GetTypeSize is used, it can be found in the DXHelp namespace.

using DX = Microsoft.DirectX;
using D3D = Microsoft.DirectX.Direct3D;

   public float X;
   public float Y;
   public float Z;
   public float Rhw;
   public UInt32 Colour;
   public static readonly D3D.VertexFormats Format = D3D.VertexFormats.Transformed | D3D.VertexFormats.Diffuse;
   public static readonly int StrideSize = DX.DXHelp.GetTypeSize(typeof(TransformedColored));
Imports DX = Microsoft.DirectX
Imports D3D = Microsoft.DirectX.Direct3D

   Private Shared _strideSize As Integer
   Public X As Single
   Public Y As Single
   Public Z As Single
   Public Rhw As Single
   Public Colour As Integer
   Public Const Format As D3D.VertexFormats = _
       D3D.VertexFormats.Transformed Or D3D.VertexFormats.Diffuse

   _strideSize = DX.DXHelp.GetTypeSize(GetType(TransformedColored))

You may have noticed that "Colour" has a "u" in it. I'm Canadian and it's the right and proper thing to do. However a great many will not agree and I support their right to be different. That is why there is also a Property defined with the American spelling. Whether you want to assign to Colour or Color, it will work for you.

To get the data in to the struct most people will use the constructor. One of the four is shown below.

   public TransformedColored(float p_x,float p_y,float p_z,float p_rhw,uint p_colour){

      X = p_x;
      Y = p_y;
      Z = p_z;
      Rhw = p_rhw;

      Colour = p_colour;
   }
   Public Sub New(ByVal xVal As Single, ByVal yVal As Single, ByVal zVal As Single, _
       ByVal rhwVal As Single, ByVal colourVal As Integer)
      X = xVal
      Y = yVal
      Z = zVal
      Rhw = rhwVal
   
      Colour = colourVal
   End Sub

The other 3 are very similar so I won't show their full definitions.

   public TransformedColored(Microsoft.DirectX.Vector4 p_position,uint p_colour);
   public TransformedColored(float p_x,float p_y,float p_z,float p_rhw,System.Drawing.Color p_colour);
   public TransformedColored(Microsoft.DirectX.Vector4 p_position,System.Drawing.Color p_colour);
   Public Sub New(ByVal position As Microsoft.DirectX.Vector4, ByVal colourVal As Integer)
   Public Sub New(ByVal xVal As Single, ByVal yVal As Single, ByVal zVal As Single, _
             ByVal rhwVal As Single, ByVal colourVal As System.Drawing.Color)
   Public Sub New(ByVal position As Microsoft.DirectX.Vector4, _
             ByVal colourVal As System.Drawing.Color)

Finally to round it all out (and make it compatible with Microsoft's) GetPosition and SetPosition were added.

   public Microsoft.DirectX.Vector4 GetPosition(){

      return new Microsoft.DirectX.Vector4(X,Y,Z,Rhw);

   }

   public void SetPosition(Microsoft.DirectX.Vector4 p_position){

      X = p_position.X;
      Y = p_position.Y;
      Z = p_position.Z;
      Rhw = p_position.W;

   }
   Public Function GetPosition() As Microsoft.DirectX.Vector4
      Return New Microsoft.DirectX.Vector4(X, Y, Z, Rhw)
   End Function

   Public Sub SetPosition(ByVal position As Microsoft.DirectX.Vector4)
      X = position.X
      Y = position.Y
      Z = position.Z
      Rhw = position.W
   End Sub

Wrapping Up

All that is really needed are the definitions in the first block at the top of the article. You need your members to hold your data for your vertices and little else. All of the additional code is simply to make it more usable. It may just be me but I think that it is generally worth it to invest time in making commonly used classes easier to use. If the DirectX team happens to read this, I'd be happy to convert the rest of your Vertex Formats into my "nicer" style for the next SDK release. ;)

This vertex format is defined in the CustomVertex.cs file which is in the Common code linked below.

Lesson Downloads

Back