Saturday, April 16, 2011

Create your own Vertex Structure

Because we send our geometry as vertices to the graphics card we need a structure that can hold all the information that we need. XNA already has 4 vertex types by default, we can use one of these, but what happens when if we need another vertex type? Well we will need to create our own vertex structure that has everything we need.

To create as a relevant example as possible I'm going to create a vertex structure that called VertexPositionTextureTextureNormal. Using 2 textures can be useful if you need a control texture (let's say a normal map) that uses different coordinates than your usual texture. To create a new vertex structure (I'll consider you already know what ) we will need to:
  1. Create a new structure that explicitly implement the IVertexType interface
  2. Add to it the relevant fields and create our constructor
  3. Add a VertexDeclaration to our structure
1. Create a new structure that explicitly implement the IVertexType interface

The first step is very easy, create a new structure with whatever name you want, just make it relevant and set it to implement the IVertexType interface, right-click the name of the interface and select Implement Interface -> Implement Interface Explicitly and you will get the following result:
public struct VertexPositionTextureTextureNormal : IVertexType
{
    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { throw new NotImplementedException(); }
    }
}
As you can see, the interface method will throw an exception if it is called, leave it like so at the moment, we will change it later.

2. Add to it the relevant fields and create our constructor

Now we must add fields in which to store the data we need and a constructor that will help us when we need to create new instances:
    public Vector3 position;
    public Vector2 texture;
    public Vector2 textureControl;
    public Vector3 normal;
 
    public VertexPositionTextureTextureNormal(Vector3 Position, Vector2 Texture, Vector2 TextureControl, Vector3 Normal)
    {
        this.position = Position;
        this.texture = Texture;
        this.textureControl = TextureControl;
        this.normal = Normal;
    }
These few lines of code are very simple and should require no further explanation, just add them in the structure.

3. Add a VertexDeclaration to our structure

This part is the most important, it is the heart of out structure. The vertex declaration will tell the graphics card how it should read and interpret the data. It's constructor will receive VertexElement objects, these tell how each of our properties (position, normal etc) should be interpreted.
    public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
    (
    new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
    new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
    new VertexElement(20, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 1),
    new VertexElement(28, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)
    );
The VertexElement constructor has 4 input fields. The first one, called offset, is the difference (in bytes) from the start of the vertex structure to the start of the current element (so the first one will have 0). Let's take the third one for example, before it we have a Vector3 and a Vector2, this means that before it we actually have 5 floats, as you know the size of a float is 4, because of that the offset of the third element is 20 (5 floats times 4 bytes/float is 20 bytes).

The second input is a VertexElementFormat enumeration to tell it how it in what format is the data stored in memory. Why does it need it? Because it does not know if it should interpret it as an integer or as a floating point number, unsigned or signed and also it will need the size of the element.

Third one tells it to what semantic it should associate to this element, for now think of the as a string that tells the GPU the intended use of the variable. For example it will know to use the Position to decide what pixel (position on screen) it will change the color of. But what will happen if we have more that one element with the same semantic? That's were the last parameter comes into play, the usage index, it tells the GPU the index number of that semantic. If it is the first element to use that semantic it will have usage index 0, if it's the second 1 and so on. As you can see position, normal and the first texture coordinate have usage index set to 0 while the second texture coordinate has usage index set to 1. Now that we have our vertex declaration we can update the interaface method like so:
    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return VertexDeclaration; }
    }
And our new vertex structure is created. If you are wondering why I don't use encapsulation for my fields I will tell you, they are useless here. Not only do they NOT change the functionality they also make a longer code (not desirable in tutorials). That's why you won't see anything extra in my tutorials code (like comments).

3 comments:

  1. Very nice blog! I enjoy your articles. But you might wanna' dig into some more advanced stuff, because everything you present here already is explained somewhere else.

    But I really like the way you write, and I'm looking forward to new posts ;)

    ReplyDelete
  2. Thank you very much!!

    At the moment I just want to get the basics up there, so people reading my blog can start even with no knowledge at all.

    ReplyDelete
    Replies
    1. I think it's great that you started from the basics.

      Delete