Basic
3D Geometry
Author
:
Jack Hoxley
Written : 8th December 2000
Contact : [Web]
[Email]
Download : Graph_05.Zip
[14 Kb]
Contents
of this lesson:
1. Introduction
2. 3D graphics theory
3. Depth Buffers
4. Geometry and Vertex buffers
5. Matrices
6. Rendering
1.
Introduction
Welcome
to lesson 5. Hopefully by this point you should be capable of setting up a Direct3D8
application and rendering some 2D triangles with texture; perfect if you want
to do a 2D game, but hardly exciting when it comes down to cutting edge 3D graphics.
Which is where this lesson is aimed at. By the end of this tutorial you'll be
well on the road to creating your first 3D world  maybe not as far as cutting
edge yet, but it's a step in the right direction.
Unfortunately
for you, this lesson is going to be big  there are 3 brand new important features
that I need to introduce you to: 3D world space, Vertex Buffers, matrices, Depth
Buffers and culling. These topics are the very basis of 3D graphics; so you're
going to need to learn them  and learn them well.
On
with the learning:
2.
3D graphics theory
Yet
more theory to start a lesson; this time it's very important  ignore this lot
and you'll be stumped later on. As I mentioned above, this stuff is the very
foundation of a 3D engine.
3D
World Space
This isn't greatly difficult really  it's just an extension of what you learnt
in lesson 03. You can set up world space in many different ways; usually so
that the +Y axis is "up" and Y is "down" (although position
of the camera can always muddle this up). 3D coordinates are specified on 3
axis  X, Y and Z. If you're looking straight down the ZAxis, +Y will be up,
+X will be right, +Z will be behind you, Y will be down, X will be left and
Z will be in front of you (the distance). Of course, the camera position can
change all of this...
Vertex
Buffers
Vertex buffers are similiar to textures in that they store premade data. Using
vertex buffers can help a great deal when it comes to generating complicated
scenes  instead of remembering 100's of different arrays for each part you
can compile it all into a vertex buffer and then render it with very few calls.
These aren't complicated to setup or use  but you should know that they exist.
Matrices
This is a fun topic, matrices (plural of matrix) are actually quite an advanced
mathematics technique  which you're only likely to come across if you do (or
have done) advanced maths courses. It doesn't matter if you dont understand
the mathematical usage of them fully (I dont), as you only really need a basic
knowledge of how they work, and more importantly how to use them. There are
three main types of matrix you'll use (there are more): World Matrix, View Matrix
and Projection Matrix. The world matrix affects vertices  if you apply a rotated
world matrix all subsequently rendered vertices will be rotated by that amount
(but you're original data wont be altered). The view matrix is basically a camera
 it defines where the "eye" is, and where it's looking at (two cordinates
in 3D space); you can also specify which direction is up  in most cases you
make +Y the up axis; but you dont have to. Finally, the projection matrix describes
how and what Direct3D renders onto the screen  the field of view (FOV) and
the view angle. We'll be using all of these later on, where I'll discuss their
usage further.
Depth
Buffers
These are very useful indeed; easy to set up, and once set up require no additional
attention  they just sit there and work. Think about this: you render two triangles
in 3D space  how do you know which triangle is in front of the other, or if
they intersect which parts of which triangle you render? A depth buffer solves
this  when you send a triangle to be renderered it stores the distance from
the camera in the depth buffer  if another triangle is rendered it decides
which pixels it'll occupy and decide what depth it's at  if this depth is less
than any existing pixel it replaces it, otherwise it's not drawn (there's something
in front of it). Depth buffers come in different depths  usually 16 bit, 24
bit and sometimes 32 bit; the higher the depth the better the quality and accuracy
but the more memory it uses and time it requires to calculate. Depth buffers
are also known as ZBuffers.
Culling
This isn't greatly important unless you're designing and coding you're own geometry.
By default Direct3D doesn't render any triangles that are facing away from the
camera  this is good as it speeds things up a great deal. Although you can
change what it culls, Direct3D normally culls triangles with vertices in a counterclockwise
direction and keeps clockwise ordered vertices. It's simple logic to work out
which one yours are  if the vertices go round in a clockwise order (leftRightBottom
for example) or counterclockwise order (BottomRightLeft). You can tell Direct3D
not to cull any triangles  but this will produce quite a large performance
loss, yet it's extremely useful when debugging....
3.
Depth Buffers
Depth
buffers, as I mentioned above, are extremely simple to setup, and once their
operating you can pretty much ignore their existence. To initialise a depth
buffer you can use this code; a slight modification of the existing initialisation
code:
'## INITIALISE() CODE ##
D3DWindow.EnableAutoDepthStencil
= 1
D3DWindow.AutoDepthStencilFormat = D3DFMT_D16 '//16 bit ZBuffer
'//Then, after you've created the D3DDevice:
'//We need to enable our Z Buffer
D3DDevice.SetRenderState D3DRS_ZENABLE, 1 



Well
done, you now have a functioning depth buffer attached to you're render device.
If you remember to do this each time you'll be fine. The only thing to note
is that you must have hardware support for the selected ZBuffer depth (if you're
using a hardware device). If you select a depth that isn't available two things
will happen : 1) the device falls back to a mode it supports or 2) it disables
depth buffering. Both of which are unpreferable. To check if the device supports
the ZBuffer setup you want you can use the following code:
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D16)
=
D3D_OK
Then
'We can use a 16 bit ZBuffer
D3DWindow.AutoDepthStencilFormat
=
D3DFMT_D16 '//16 bit ZBuffer
Else
'We could now check for different modes available...
End If 



If
you want to search for other depth buffer modes you can use this little piece
of enumeration code:
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D16)
=
D3D_OK
Then
Debug.Print
"16
bit
ZBuffers
are
supported
(D3DFMT_D16)"
End If
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D16_LOCKABLE)
=
D3D_OK
Then
Debug.Print
"Lockable
16 bit
ZBuffers
are
supported
(D3DFMT_D16_LOCKABLE)"
End If
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D24S8)
=
D3D_OK
Then
Debug.Print
"32
bit
divided
between
24 bit
Depth
and 8
bit
stencil
are
supported
(D3DFMT_D24S8)"
End If
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D24X4S4)
=
D3D_OK
Then
Debug.Print
"32
bit
divided
between
24 bit
depth,
4 bit
stencil
and 4
bit
unused
(D3DFMT_D24X4S4)"
End If
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D24X8)
=
D3D_OK
Then
Debug.Print
"24
bit
ZBuffer
supported
(D3DFMT_D24X8)"
End If
If
D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
DispMode.Format,
D3DUSAGE_DEPTHSTENCIL,
_
D3DRTYPE_SURFACE,
D3DFMT_D32)
=
D3D_OK
Then
Debug.Print
"Pure
32 bit
Zbuffer
supported
(D3DFMT_D32)"
End If




There;
you now know about as much as you'll ever need to know about depth buffers.
4.
Geometry and Vertex Buffers
Geometry
creation in full 3D is not really that different from when we did it with 2D,
the only difference being that we use 3 dimensions rather than 2. Later on when
we do lighting it gets a little bit more complicated.
The
first thing that we must do is define a new vertex format to use; we'll be using
the equivelent of the D3DLVERTEX type from DirectX7; where we give it a position
and a colour and Direct3D transforms it.
'//We're
using
3D
vertices,
but
we're
not
using
Lighting
just
yet...
'
which
means
we
need
to
specify
the
vertex
colour
Private Type LITVERTEX
x As Single
y As Single
z As Single
color As Long
Specular As Long
tu As Single
tv As Single
End Type
'//The Descriptor for this vertex format...
Const Lit_FVF = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX1)
Dim Cube(35) As LITVERTEX 



Notice
that I've also put in the declaration for our geometry; 36 vertices for a cube
 sounds a lot; it is. Methods later on (using indices/index buffers) will allow
us to generate the same cube for only 8 vertices. If we were using triangle
strips we could do this cube for 24 vertices  but writing it to a vertex buffer
would have been more awkward.
We'll
now rewrite our InitGeometry( ) function so that it creates a cube. Two things
to note with this code  1) I didn't bother with checking the the vertex order
(for culling) and 2) all vertices are generated around the origin  the middle
of the cube is going to be [0, 0, 0] and the rest of the vertices are equally
distributed around this (+ 1m); when you've finished reading about matrices
you'll understand why.
Code
In
This
Font/Size
in
these
tables.
'//I used the Immediate window to
' get the RGB() values for each of these...
Const C000 As Long = 255 '//Red
Const C001 As Long = 65280 '//Green
Const C100 As Long = 16711680 '//Blue
Const C101 As Long = 16711935 '//Magenta
Const C010 As Long = 65535 '//Yellow
Const C011 As Long = 16776960 '//Cyan
Const C110 As Long = 16777215 '//White
Const C111 As Long = 8421631 '//Orange
'//1. Fill the structures with the data
'Front
Cube(0) = CreateLitVertex(1, 1, 1, C011, 0, 0, 0)
Cube(1) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(2) = CreateLitVertex(1, 1, 1, C001, 0, 0, 0)
Cube(3) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(4) = CreateLitVertex(1, 1, 1, C001, 0, 0, 0)
Cube(5) = CreateLitVertex(1, 1, 1, C101, 0, 0, 0)
'Back
Cube(6) = CreateLitVertex(1, 1, 1, C010, 0, 0, 0)
Cube(7) = CreateLitVertex(1, 1, 1, C110, 0, 0, 0)
Cube(8) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(9) = CreateLitVertex(1, 1, 1, C110, 0, 0, 0)
Cube(10) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(11) = CreateLitVertex(1, 1, 1, C100, 0, 0, 0)
'Right
Cube(12) = CreateLitVertex(1, 1, 1, C010, 0, 0, 0)
Cube(13) = CreateLitVertex(1, 1, 1, C011, 0, 0, 0)
Cube(14) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(15) = CreateLitVertex(1, 1, 1, C011, 0, 0, 0)
Cube(16) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(17) = CreateLitVertex(1, 1, 1, C001, 0, 0, 0)
'Left
Cube(18) = CreateLitVertex(1, 1, 1, C110, 0, 0, 0)
Cube(19) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(20) = CreateLitVertex(1, 1, 1, C100, 0, 0, 0)
Cube(21) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(22) = CreateLitVertex(1, 1, 1, C100, 0, 0, 0)
Cube(23) = CreateLitVertex(1, 1, 1, C101, 0, 0, 0)
'Top
Cube(24) = CreateLitVertex(1, 1, 1, C011, 0, 0, 0)
Cube(25) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(26) = CreateLitVertex(1, 1, 1, C010, 0, 0, 0)
Cube(27) = CreateLitVertex(1, 1, 1, C111, 0, 0, 0)
Cube(28) = CreateLitVertex(1, 1, 1, C010, 0, 0, 0)
Cube(29) = CreateLitVertex(1, 1, 1, C110, 0, 0, 0)
'Top
Cube(30) = CreateLitVertex(1, 1, 1, C001, 0, 0, 0)
Cube(31) = CreateLitVertex(1, 1, 1, C101, 0, 0, 0)
Cube(32) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(33) = CreateLitVertex(1, 1, 1, C101, 0, 0, 0)
Cube(34) = CreateLitVertex(1, 1, 1, C000, 0, 0, 0)
Cube(35) = CreateLitVertex(1, 1, 1, C100, 0, 0, 0)




Looks
great doesn't it. Just imagine how complicated it'd be to make a sphere or a
person... which is why you'll love using externally created objects and loading
them straight in. You'll notice that I've used constants for all the colours
 a cube is made up of 8 corners, so I designed 8 constants for colour  a colour
for each corner. As 34 vertices can share the same point I just placed the
constant as the colour; if you change the constant all required vertices will
change colour as well.
You
may also have noticed that I haven't mentioned vertex buffers yet. Well, here
they are. Vertex buffers are basically an allocated amount of memory filled
with vertex data; specially formatted by Direct3D so it can access them easier
and faster (most of the time). The first step is to allocate enough memory for
the vertex buffer, then we fill it with the Cube( ) vertex data that we've just
defined. To do this, we use the following code:
'//2. Create us a blank vertex buffer of the required size
Set VBuffer = D3DDevice.CreateVertexBuffer(Len(Cube(0)) * 36, 0, Lit_FVF,
D3DPOOL_DEFAULT)
If VBuffer Is Nothing Then Exit Function '//Error handler
'//3. Fill the created vertex buffer with the data
D3DVertexBuffer8SetData VBuffer, 0, Len(Cube(0)) * 36, 0, Cube(0)




Note
that we must specify the size of the vertex buffer in bytes  this MUST be accurate
or we'll cause a mess; too small and you'll get errors when filling it up, too
big and you'll waste memory. The size is going to be "Size of one vertex
* number of vertices"  we use the Len( ) function to work out the size,
then we multiply this by the number of vertices we have (36). we must also supply
the vertex format (FVF) so that Direct3D knows what it's looking at; finally
we then decide how Direct3D should manage our vertex buffer; D3DPOOL_DEFAULT
should suffice, otherwise you can specify D3DPOOL_MANAGED (Let it choose, and
move it around as it sees fit) or D3DPOOL_SYSTEMMEM (place it in system memory
(RAM)).
Next,
we fill the vertex buffer using a hidden function (sort of hidden anyway). We
give it the name of our vertex buffer and the size of the area we wish to fill
and the offset (should we wish to start somewhere other than the beginning).
finally we pass the array of data that we want to pass; this must be equal in
size and format to that which we created the vertex buffer for  or nasty things
will probably happen.
Assuming
nothing went wrong above we can move onto more interesting things....
5.
Matrices
Matrices
are an extremely useful thing to understand; they allow you to do an enourmous
amount of things to 3D worlds. Matrices are quite a complicated mathematical
topic  so I wont go into how/why matrices work  just how you can use them.
A matrix, in Direct3D, is a 4x4 matrix  think of it like a grid, with 4 entries
along the top and 4 entries down the side. Direct3D uses these numbers to alter
various things during lowlevel processing  between the time you make calls
to render and the time they appear on screen.
There
are a few things that you should bare in mind when using matrices:
1. You can combine matrices together to form a new matrix; for example, if you
multiply a rotation and a translation matrix you will get a single matrix that
rotates and translates anything it's given.
2. Unlike normal numbers where (A * B) = (B * A) multiplying matrices [A] *
[B] does not equal [B] * [A]  so it's important to do things the right way
around.
3. There are lots of functions in the D3DX8 library to help with manipulating
matrices
There
are three types of matrix that you'll always have to use; others are for different
effects. The main types are outlined below:
World
Matrix
This matrix alters the vertices  this is the one you'll use the most often.
All transformations of vertices work around the origin [0,0,0]  it rotates
vertices around the origin, scales vertices around the origin. This is why (earlier)
we created our cube around the origin. As already mentioned it is possible to
combine multiple matrices to form a single allinone matrix; but as you also
should remember, the order you multiply them matters. If you rotate something
around the X axis and the Z axis you'll not get the same result if you'd rotated
around the Z then the X. For example, if you have a car wheel and rotate it
around the X axis you'll get it rotating like a wheel should, if you then rotate
it around the Z axis it'll appear to be tilted in one direction  but it'll
still rotate like a wheel should. If you rotate around the Z axis the wheel
will appear to tilt, if you then rotate it around the X axis the wheel will
seem to wobble as it goes around; the effect is best seen when animated though.
The
following code is from the sample for this lesson:
Dim
matWorld
As
D3DMATRIX Dim
matTemp
As
D3DMATRIX
D3DXMatrixIdentity
matWorld '//Reset our world matrix
D3DXMatrixIdentity
matTemp
D3DXMatrixRotationX
matTemp,
RotateAngle
* (pi
/ 180)
D3DXMatrixMultiply
matWorld,
matWorld,
matTemp
D3DXMatrixIdentity
matTemp
D3DXMatrixRotationZ
matTemp,
RotateAngle
* (pi
/ 180)
D3DXMatrixMultiply
matWorld,
matWorld,
matTemp
D3DDevice.SetTransform
D3DTS_WORLD,
matWorld 



okay,
not too complicated really. First we create two matrices, one is our "Master"
matrix, the other is a temporary one, because we're doing more than one transformation
we need something to store each step before multiplying it. Next we reset our
master matrix  this is very important as matrix multiplying and transforming
is a cumulative thing  if you alter it each frame without reseting it very
quickly things will go very strange. The Identity matrix isn't actually a matrix
where everything is set to '0', all the X=Y entries are filled with '1's [1,1]
[2,2] [3,3] [4,4]. After we've reset our matrix we start manipulating it; the
available transformations are:
'//Normal Rotation D3DXMatrixRotationX(Out
As
D3DMATRIX,
angle
As
Single) D3DXMatrixRotationY(Out
As
D3DMATRIX,
angle
As
Single)
D3DXMatrixRotationZ(Out
As
D3DMATRIX,
angle
As
Single)
'//Rotation through a custom axis ' You must specify an axis using the VAxis setting ' FYI: X=[1,0,0] Y=[0,1,0] and Z=[0,0,1] D3DXMatrixRotationAxis(MOut
As
D3DMATRIX,
VAxis
As
D3DVECTOR,
angle
As
Single)
'//Misc Transforms 'This scales vertices by the amounts specified D3DXMatrixScaling(MOut
As
D3DMATRIX,
x As
Single,
y As
Single,
z As
Single) 'this moves vertices by the amounts specified D3DXMatrixTranslation(MOut
As
D3DMATRIX,
x As
Single,
y As
Single,
z As
Single)
'Others are available, and are listed in the object browser  and can be found by going DxVBLibA > D3DXMATH_MATRIX




One
thing to note about the angles specified when rotating  they're in radians
not degrees. If you're happy using radians thats fine, but if you prefer to
use degrees you can use this simple conversion: Degrees * (Pi / 180) where Pi
is 3.14159265358979 (which in turn can be worked out using (4 * atn(1)))
Finally
we get to the point where we apply the transformation to our rendering device.
This can be done as often as you wish, but the less times the better. Once this
matrix is applied all vertices rendered after that point will be transformed
accordingly  and will continue to be until another matrix is set.
View
Matrix
The view matrix can be thought of as the camera; whilst the projection matrix
(see later) controls some of the more complicated parts of the "camera",
the view matrix is the one you alter if you wish to move the camera around (as
most games do). Setting the view matrix is extremely simple, but you need to
think about it a little first  if nothing is appearing on screen then the chances
are that you've got the camera set up wrong...
To
alter the view matrix we'll need this code:
'//The function prototype D3DXMatrixLookAtLH(MOut
As
D3DMATRIX,
VEye
As
D3DVECTOR,
VAt As
D3DVECTOR,
VUp As
D3DVECTOR)
'//And this is an example: Dim
matView
as
D3DMATRIX Dim
vecFrom
as
D3DVECTOR Dim
vecTo
as
D3DVECTOR Dim
vecUP
as
D3DVECTOR
'//The camera is at this point in 3D worldspace vecFrom.X
= 0 vecFrom.Y
= 10 vecFrom.Z
= 10
'//And it is looking at this point: vecTo.X
= 0 vecTo.Y
= 0 vecTo.Z
= 0
'//Unless you want to do something clever you ' Can leave this as it is for all applications vecUp.X
= 0 vecUp.Y
=
1 vecUp.Z
= 0
'//Now we generate Our final Matrix
D3DXMatrixLookAtLH
matView,
vecFrom,
vecTo,
vecUp
D3DDevice.SetTransform
D3DTS_VIEW,
matView




As
you can see, it's quite easy to update the position of the camera. Check out
the DirectX 7 immediate mode tutorials on camera
placement  whilst it's using DirectX 7 interfaces, the maths will still be
the same to get the camera to follow a "person" around, or move according
to the user...
Projection
Matrix
Finally we come to the projection matrix; this matrix isn't used as often
as the other two, but it's equally as important. The projection matrix controls
how the scene appears when we look through our camera  you can set the field
of view (the closest and furthest objects visible) and the view angle (wide
angle or telephoto). Unless you're intending to make a game with a zoom feature
or some weird headmessingup game you're unlikely to set this matrix very often;
normally it's setup during initialisation and left.
'//The function prototype:
D3DXMatrixPerspectiveFovLH( MOut As D3DMATRIX, fovy As Single, aspect As Single, zn As Single, zf As Single)
'Where Mout is the result
'fovy is the view angle
'Aspect is the aspect ratio
'zn is the nearest boundary for polygons  anything between here and the camera position are not rendered
'zf is the furthest boundary  anything beyond here is not drawn
'//Finally we create our matrix
D3DXMatrixPerspectiveFovLH matProj, pi / 4, 1, 0.1, 500
'//And then we commit it to our Direct3D device
D3DDevice.SetTransform D3DTS_PROJECTION, matProj




That's
not greatly difficult is it. The only two stumbling blocks are the "fovy"
and "aspect" parameters; the fovy is an angle specified in radians
 for this it's much easier to keep it in radians, as you're only ever likely
to use a few values  all of them "pi / something" where something
is going to be in the range of 2  9, with 2 being wide angle and 9 being telephoto,
dont set it to 0, as you'll get nothing displayed, and quite probably a divide
by 0 error (pi / 0 ). Then there's the aspect ratio  you can usually leave
this as 1 (1:1 ratio); if you make it less than 1 it appears to stretch geometry
vertically, greater than 1 appears to stretch geometry horizontally.
Matrices
aren't that difficult really  but you'll use them for almost every full3D
scene that you render; so you'll probably learn them quite quickly. One final
thing to remember, which is common sense really: if world matrices transform
vertices you cant use them with transformed and lit vertices...
6.
Rendering
Rendering
isn't a big change, just a few new lines for you to look at, so I wont bother
spending much time going over it all again...
'//First off we'll alter the device clearing code: D3DDevice.Clear
0,
ByVal
0,
D3DCLEAR_TARGET
Or
D3DCLEAR_ZBUFFER,
0, 1#,
0
'//Secondly, we must change how we render our primatives: D3DDevice.SetStreamSource
0,
VBuffer,
Len(Cube(0))
D3DDevice.DrawPrimitive
D3DPT_TRIANGLELIST,
0, 12 



Not
greatly difficult really; as we're now using Zbuffers (or Depth buffers  whichever
you prefer) we'll need to clear that buffer as well as the rendering target
 if you forget to clear the ZBuffer you can get some weird effects happening
with the draworder. Lastly, when using vertex buffers to hold our geometry
we must render them slightly differently; commit the vertex buffer to the specified
pipeline/stream and tell Direct3D how big each entry will be; once that's done
we can use the normal (and simpler) D3DDevice.DrawPrimative statement. Assuming
everything has gone okay (it should of) you should be looking at something similiar
to this:
I
created a slightly different geometry set so that you could see each face
being rendered.
As
per normal you can download the complete source code for this tutorial from
the top of this page, I advise that you do play around with the code and get
familiar with how things work.
Assuming
you're ready to continue  onto Lesson 06 : Drawing
Text  Custom Rasterizers and Normal 2D text.
