Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
73 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Introduction
 Getting Started
 The Main Loop
 Drawing Something
 The Theory

 Drawing Something
 The Practical Part


 Printable version
 Discuss this article
 Get the source

The Series
 Part 1
 Part 2
 Part 3

Drawing Something - The Practical Part

As already mentioned we render all our geometry using triangles (I prefer to use the term triangle instead of polygon - but feel free to use whichever you prefer). These triangles will be made up of a set of vertices, and these vertices will have a specific set of properties depending on what they’re for.

It would therefore make sense that we had an array of one vertex type filled with data - which is exactly what we’re going to do. As you can imagine this will get very long - one line of code per vertex, 3 per triangle - even a simple cube can take up a hundred or so lines of code… which is the reason why I’m going to keep this simple.

Step 1: Setting things up.

First we need to create an array of vertices:

Dim TriVert(0 To 2) As TLVERTEX '//we require 3 vertices to make a triangle...

Then we need to set a couple of parameters in the initialization procedure:

D3DDevice.SetVertexShader FVF_TLVERTEX
D3DDevice.SetRenderState D3DRS_LIGHTING, 0

The first line tells the rendering device what type of vertex we’re going to be using - pass the constant that we defined earlier here. The second parameter tells Direct3D that we don’t want it to do the lighting - by default it will.

Step 2: Making the triangle

This is as simple as filling out the array with the required data, for clarity we’ll stick it in a whole new procedure, which will be called at the end of the initialization process:

Private Sub InitializeGeometry()
  TriVert(0) = CreateTLVertex(0, 0, 0, 1, &HFF0000, 0, 0, 0)
  TriVert(1) = CreateTLVertex(175, 0, 0, 1, &HFF00&, 0, 0, 0)
  TriVert(2) = CreateTLVertex(0, 175, 0, 1, &HFF&, 0, 0, 0)
End Sub

Three things to note here; firstly we have a new function here - CreateTLVertex(), this is a little helper function that I wrote to help in filling the structures with the relevant data, it looks like this:

Private Function CreateTLVertex(X As Single, Y As Single, Z As Single, _
                                rhw As Single, Color As Long, _
                                Specular As Long, tu As Single, _
                                tv As Single) As TLVERTEX
  CreateTLVertex.X = X
  CreateTLVertex.Y = Y
  CreateTLVertex.Z = Z
  CreateTLVertex.rhw = rhw
  CreateTLVertex.Color = Color
  CreateTLVertex.Specular = Specular
  CreateTLVertex.tu = tu
  CreateTLVertex.tv = tv
End Function

Secondly we have to specify the colour as a long, using the RGB( ) function wont work properly here - it is possible to reverse the values and use it - RGB(B, G, R), but if you can use hexidecimal it’s the preferred method. Think of it as the same as an HTML colour code and you’ll be fine.

Thirdly, and more subtly is the order in which the vertices where created. This is actually extremely important - get this wrong and Direct3D will cull (remove) your triangles and not render them; It is often quite likely that this is the cause if you make a program and nothing is rendered (yet appears to be set up correctly). If we plot the triangle coordinates in order we will see the pattern - Clockwise:

You can set which type of triangle Direct3D will cull (Clockwise, Counter-Clockwise or none), but by default it will cull counter-clockwise triangles, therefore rendering only those that are in a clockwise order. You may well think that it’s easy just to stop it from removing any triangles - While that is true it’s bad practise. If you can get into the habit of generating your vertices in the correct order from the beginning then so much the better; but it’s still useful to know how you specify the culling modes:

D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW

Fairly simple really, whichever you specify the opposite will be rendered; for example, our triangles are clockwise ordered, therefore you should specify the above as being D3DCULL_CCW (but that’s the default, so you don’t need to).

The last thing to note before we move on is to do with transformed and lit vertices. As I told you, transformed and lit vertices are 2D - X and Y, so why is there a Z coordinate? The Z-Coordinate should be on a 0.0 to 1.0 scale, and when a depth buffer is attached (more on that later) triangles will be drawn over each other based on this value, for example - a triangle with a Z of 0 will go over a triangle with a Z of 1. And a Triangle with 3 different Z values will go through any other triangles that it intersects…. If two triangles have the same Z value, whichever is rendered last will appear on top.

Step 3: Rendering the triangle

We’ll now go back to our Render() procedure - as discussed earlier - and update it to render our new triangle. At the moment we aren’t doing anything clever, no textures, no transformations - so everything can be done with one call.

Later on we’ll use a more optimised method of rendering, and some more clever tricks; but right now we’ll stick to the basics:

D3DDevice.BeginScene
  'All rendering calls go between these two lines
  D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, 1, TriVert(0), Len(TriVert(0))

D3DDevice.EndScene

Simple as that really. We use the function "DrawPrimativeUP" to render a custom type of vertex straight from memory - it is of type TRIANGLELIST (more in a second), is made up of one triangle and uses the specified array with the specified size. In more detail:

The first parameter is the primitive type, While Direct3D does all solid rendering as triangles, but we can also render points and lines. The full range of options are listed here:

D3DPT_POINTLIST: Direct3D draws each vertex as an unconnected point in 3D space, just a single pixel dot on the screen. Useful for particle effects (but there are better ways).

D3DPT_LINELIST: Every pair of entries specifies a pair of coordinates for Direct3D to draw a line between (0 & 1, 2 & 3, 4 & 5 etc…)

D3DPT_LINESTRIP: Same as a line list, but the beginning of one line joins up with the end of the previous - creating a continuous line through all the specified points.

D3DPT_TRIANGLELIST: Every triplet of vertices defines a new triangle - this will be filled in solid, with colour components blended across it’s surface; this is the method we currently use. (0-1-2 & 3-4-5 & 6-7-8-9 etc…).

D3DPT_TRIANGLESTRIP: Same as a triangle list, but it creates a series of triangles all joining up; from the second triangle onwards each triangle uses the last vertex of the previous triangle as it’s first vertex (0-1-2 & 2-3-4 & 4-5-6 etc…). Colours are blended across the surface of these in the same way as a triangle list.

D3DPT_TRIANGLEFAN: draws a series of triangles all connected to the first vertex - perfect for octagonal, hexagonal or circular type shapes. 0-1-2 & 0-3-4 & 0-5-6 etc… These triangles are drawn solid, and colour is blended across them.

There are numerous advantages and disadvantages to all of these methods; some of more obvious than others. As a start you should already have guessed that the less triangles you use the faster it goes - so try to keep them to a low number (without looking nasty); more subtly, you should also keep the vertex count as low as possible when creating geometry. This is on the basis that Direct3D will send all of the data through the various cables, pipes and chips to the 3D card - and the more data you have to send the longer it takes; so the less vertices you use the faster the data can be transmitted. When drawing a continuous line it would make perfect sense to use a line strip. When dealing with triangles decide on what you need to do - often it is faster and simpler to render something using a triangle strip, other times it makes it more complicated and you should use a triangle list; then there is the option of using a triangle fan for circular type objects. Experiment and see…

The second parameter is the primitive count, think of it as the number of triangles, or number of points (depending on the primitive type). In this example we only created one triangle, so have specified the fact that there is only 1 triangle.

The third parameter specifies where Direct3D should look for the vertex data, this must be an entry in the array; usually the first entry - but it doesn’t have to be; just remember that there needs to be the correct number of vertices left for the specified primitive count.

The last parameter specifies how big (in memory bytes) our vertex structure is - this is for Direct3D’s internal usage. You don’t really need to understand how it works, but basically the third parameter points to the beginning of the memory to look for, and Direct3D knows how many entries there will be (primitive count value), using this parameter it can get the size of the structure, and therefore work out where all the individual bits of data are, and how much memory the whole lot should take up. Use Len( ) on the first element in the array for this.

Now that you can render simple 2D geometry you should practise it - try making a square using the various methods, draw a circle-like object with a triangle fan; and try making an arch or rounded rectangle using a triangle strip. If you look at almost any basic geometric shape it will always break down into a series of triangles (sometimes not very nicely), but with some basic maths skills it is easy to write an algorithm that generates an arch (or whatever) from a series of triangles…

Overview

You may well think that very little has been done in this article - you would be very wrong thinking this. In the next articles we will cover all of the major aspects of Direct3D programming - if there was anything that you didn’t follow in this article (code wise) then it’s going to catch you out later. Trust me. I have written enough DirectX applications that I can write a complete DirectX 7 and DirectX 8 program similar to this off the top of my head in very little time.

The next article will advance our knowledge of geometry into the 3rd dimension; along the way we’ll learn about vertex buffers, index buffers, fullscreen mode, depth buffers, normals and some basic lighting - sound like fun? You bet J

Any feedback on this article is much appreciated, or if you have any questions I’d be happy to try and help you out (but remember, I’m good, but I don’t know everything [yet]). Drop me a message: Jollyjeffers@Greenonions.netscapeonline.co.uk or visit my site at www.vbexplorer.com/directx4vb/ for a massive collection of 55 DirectX 7 tutorials, and ongoing DirectX 8 articles (11 lessons at time of writing) and some general game programming articles for good measure!

You can download the full source code for this article here.

See you next time J

This article was written by Jack Hoxley, January 2001. This article is copyright © Jack Hoxley - all rights reserved. You may not reuse this material without prior permission from myself - please email me if you wish to discuss this.