Immediate Mode: Rotating a 3D object
By: Carl Warwick [Email] [Web: Freeride Designs]
Written: June 26 2000
I'm back with the second tutorial on using D3DIM in
Visual Basic, if you missed the first installment then be sure to read it before going
through this one.
You should now know how to initialise DDraw and D3DIM in
fullscreen mode (Please Note. From this tutorial initialising DDraw and D3DIM will be done
in different subs to make it easier to understand). Now we are going to build on our
knowledge by setting up a viewport, a projection matrix, a view matrix and a world matrix,
we'll then create our first 3D object, a pyramid. Our pyramid will be rotated by making
changes to our world matrix, and finally be rendered as a Triangle Fan.
Our pyramid will be made from LVertices, this means we
have to light them, but D3D will transform them from world space to screen space for us.
Once again our project will have 1 form
"frmMain", and 1 module "modMain", and all our declarations will go in
DirectX7 'Direct X
DirectDraw7 'Direct Draw
DirectDrawSurface7 'Primary surface
Direct3D7 'Direct 3D
Direct3DDevice7 'Direct 3D Device
D3DLVERTEX 'Array of vertices (L = Lit)
Boolean 'Boolean to check if program is still running
3.14 'Constant to hold the value of pi
180 'multiply degrees by Rad to get angle in radians
You will probably notice that I've only changed one thing
from the last tutorial, and that is I've replaced TLVertexList(3) with LVertexList(5), Our
pyramid has 6 vertices, the reason its not 5 vertices is that 2 of them are the same, this
is needed to complete our last face on the pyramid. We could resolve this problem by using
DrawIndexedPrimitive, but it would be more effort than its worth. (We'll examine
DrawIndexedPrimitive in a later tutorial).
You already know how to initialise DDraw, and you know
the basics of initialising D3DIM. I've added quite a bit to our D3DIM initialisation for
this tutorial, so I'll go through it here.
'Get the last device driver (last one is usually the best one)
'you could specify or search for a particular device, eg
RGB or HAL device
'Set the Device
'Define the viewport rectangle.
'This will just be the same size as our screen
You've already seen most of the above code,
the only new bit should be where we define the viewport. This is pretty simple, and you
will probably never need to change this. All the viewport does is tell DX what part of the
screen to render everything to, obviously we want this to be the same size as the screen
and starting in the top-left corner.
Dim matWorld As D3DMATRIX
Device.SetTransform D3DTRANSFORMSTATE_WORLD, matWorld
'The projection matrix defines how the 3D scene is
"projected" onto the
'2D render target (the backbuffer surface).
Dim matProj As D3DMATRIX
'Polygons that are a closer distance than 1 to the camera,
'and are a further distance than 100 will not be rendered.
'We're using a 90 degree (pi/2) field of view (FOV).
'Most people use either a 90 or 60 degree FOV for all
Call DX.ProjectionMatrix(matProj, 1, 100, pi / 2#)
Device.SetTransform D3DTRANSFORMSTATE_PROJECTION, matProj
'The view matrix defines the position, look at position, and roll of the camera.
Dim matView As D3DMATRIX
'We use the MakeVector function to define the positions of our
'Our camera is -20 units along the Z axis, and looking at
the origin (0,0,0).
'We'll leave the Up vector as (0,1,0) and the roll as 0.
Call DX.ViewMatrix(matView, MakeVector(0, 0, -20),
MakeVector(0, 0, 0), MakeVector(0, 1, 0), 0#)
Device.SetTransform D3DTRANSFORMSTATE_VIEW, matView
'Because we are using the LVertex type we don't want
'have any effect on our scene, so we turn lighting off.
Device.SetRenderState D3DRENDERSTATE_LIGHTING, False
'Lets use Gouraud shading, it much nicer than Flat
'DX7 does not support phong shading, so don't try to use
Now we need to define our World Matrix,
Projection Matrix and View Matrix, both the projection matrix and the view matrix will not
change after they have been made here, but we will change the world matrix to rotate the
The world matrix defines the position, scale
and rotation of all the objects, so rather than moving each objects vertices to move an
object we just change the world matrix and it does it for us. This will be explained in
more detail further down.
The projection matrix (matrix, near clipping
plane, far clipping plane, field of view) just describes how far away from the camera
objects need to be before they are clipped (not rendered). The near clipping plane and far
clipping plane are the boundaries that an object must be in before being clipped.
The field of view (FOV) is used to determine the angle that is seen. Its best to use
angles of 90 and 60 degrees for the FOV.
The view matrix (matrix, From, To, Up, Roll)
determines the cameras position and direction.
From is the position of the camera.
To is where the camera is looking at.
Up should never be set to 0, and probably best left as (0,1,0).
Roll is the angle (in radians) that the camera is rotated around the z-axis.
We use a function that is defined in the code called MakeVector, you pass this function
the X,Y and Z co-ordinates and it returns a D3DVector with these co-ordinates.
Next we need to make our pyramid.
'The first element of an array of a Triangle fan has to be
'the tip of the object.
'I made the first vertex by defining each point, just
to show you that it
'can be done this way, but its much easier to use
'Therefore we could create the first vertex with the
'Call DX.CreateD3DLVertex(0, 5, 0, DX.CreateColorRGBA(1, 0,
0, 1), 1, 0, 0, LVertexList(0))
'Then we make the four base points of the pyramid in a clockwise order.
'We need to copy the first base vertex to make the last side of the pyramid.
This is all pretty easy, we just define the positions and
colors of each vertex. Our pyramid is a Triangle Fan structure, if you need to know how a
triangle fan works then please refer to the previous tutorial.
We are defining our pyramids positions in world space,
not screen space, this is because we are using the LVertex type rather than the TLVertex
You should see that two of our vertices are the same,
this is so that the last face on our pyramid shows up, if you didn't have this last one
then we would have a pyramid with a hole in it.
Now lets take a look at our Main_Loop.
Integer 'The angle to rotate by
D3DMATRIX 'Stores the world matrix, change this to move,
D3DMATRIX 'Used in world matrix calculations.
'Start our loop, press the 'Esc' key to exit
'decrease stepval by 1 to rotate the rectangle
'If stepval is less than 0
degrees then make stepval = 360 degrees
'We want to rotate the object around its Y-axis
'You can easily make it rotate
around the X or Z axis by
'changing RotateYMatrix to
RotateXMatrix or RotateZMatrix
'If you want to rotate around Y
axis and Z axis then
'include the next 2 lines
stepval * Rad
'Transform the world
'Clear the device AFTER all
the changes to any vertices, and BEFORE
and any bltting.
'Begin the scene, must do
this after Calling Clear_Device, and before
'Render the pyramid to the device (backbuffer), its a triangle fan.
'Call EndScene when finished using D3D routines
'Flip the primary surface
'Let windows do its stuff
Once again we are going to use stepval as our angle (in
degrees) that our object should be rotated by.
stepval * Rad This line will fill our matWorld matrix with the correct data to
rotate the pyramind by stepval degrees (we need to multiply stepval by Rad to convert it
To combine matrices you have to multiply them together, luckily dx provides us with a matrix multiply function.
If you then add the next 2 lines the pyramid will first
rotate around the Y-axis, then it will rotate around the Z-axis. You should note that the
order you multiply these matrices in does make a difference. If you multiply Y by Z you
will get a different rotation than if you multiply Z by Y.
If we wanted to move the object aswell then we would need to create a transformation
matrix and then multiply our Rotation matrix by our Transformation matrix (make sure you
get the order right, Rotation then Transformation). I'll show you how to move objects in
Now we use Device.SetTransform D3DTRANSFORMSTATE_WORLD, matWorld to transform the world matrix using our matrix
Finally we render the pyramid with Call Device.DrawPrimitive(D3DPT_TRIANGLEFAN,
D3DFVF_LVERTEX, LVertexList(0), 6, D3DDP_DEFAULT) We are using a triangle fan, and using the D3D_LVertex type.
Remember that there are 6 vertices in our array, we could use the Ubound function to get
the number of vertices (this will be used in the later tutorials).
We have now seen how to use D3Ds LVertex type, and how to
set up a view, projection and world matrix, and how to use the world matrix to rotate
objects. I made this tutorial as short and simple as possible, this was to make sure its
as easy as possible to learn the basics. From now on the tutorials will gradually get
harder, and there will be more and more for you to learn, but with a little bit of
patience you should get through it all.
The pyramid in this tutorial did not have a bottom, if
you change the code so that the pyramid rotates around the X-axis then you would see that
nothing shows when looking at the bottom of the pyramid. This is an easily solved problem,
but it would require us to use a z-buffer which we haven't covered yet, but don't worry we
will cover z-buffers in the next tutorial, along with moving and scaling objects.
Until next time, good luck.
- Freeride Designs