Main Site Links Resources Tutorials
News VB Gaming Code Downloads DirectX 7
Contact Webmaster VB Programming Product Reviews DirectX 8
  General Multimedia Articles DirectX 9
      Miscellaneous

Immediate Mode: Rotating a 3D object
By: Carl Warwick [Email] [Web: Freeride Designs]
Written: June 26 2000

Download: IM_Rotate3D.Zip (9kb)


Contents


Introduction

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.

screenshot


Declarations

Once again our project will have 1 form "frmMain", and 1 module "modMain", and all our declarations will go in "modMain".

Option Explicit

Public DX As DirectX7                    'Direct X
Public DD As DirectDraw7                 'Direct Draw
Public Primary As DirectDrawSurface7     'Primary surface
Public BackBuffer As DirectDrawSurface7  'Backbuffer
Public Direct3D As Direct3D7             'Direct 3D
Public Device As Direct3DDevice7         'Direct 3D Device

Public LVertexList(5) As D3DLVERTEX      'Array of vertices (L = Lit)

Public bRunning As Boolean               'Boolean to check if program is still running

Public Const pi = 3.14                   'Constant to hold the value of pi
Public Const Rad = 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).


Initialisation

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.

Public Sub Init_D3D(ScreenWidth As Integer, ScreenHeight As Integer)
    Dim DEnum As Direct3DEnumDevices
    Dim Guid As String
    
    Set Direct3D = DD.GetDirect3D
    
    '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 DEnum = Direct3D.GetDevicesEnum
    Guid = DEnum.GetGuid(DEnum.GetCount)
        
    'Set the Device
    Set Device = Direct3D.CreateDevice(Guid, BackBuffer)
    
    'Define the viewport rectangle.
    'This will just be the same size as our screen

    Dim VPDesc As D3DVIEWPORT7
        
    VPDesc.lWidth = ScreenWidth
    VPDesc.lHeight = ScreenHeight
    VPDesc.lX = 0
    VPDesc.lY = 0
    VPDesc.minz = 0#
    VPDesc.maxz = 1#
    
    Device.SetViewport VPDesc
  

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.

    'Set up transformation matrices.
    
    'The world matrix controls the position and orientation of the polygons
    'in world space.
    'By changing the world matrix you can rotate, move and scale objects.

    Dim matWorld As D3DMATRIX
    
    DX.IdentityMatrix matWorld
    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 applications

    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 camera,
    '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 light to
    '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 shading (D3DSHADE_FLAT)
    'DX7 does not support phong shading, so don't try to use it.

    Device.SetRenderState D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD
End Sub

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 pyramid.

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.


Making our pyramid

Next we need to make our pyramid.

Public Sub Initialise_Geometry()
    'The first element of an array of a Triangle fan has to be
    'the tip of the object.

    LVertexList(0).X = 0
    LVertexList(0).Y = 5
    LVertexList(0).Z = 0
    LVertexList(0).Color = DX.CreateColorRGBA(1, 0, 0, 1)
    LVertexList(0).specular = 1
     '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 DX.CreateD3D**Vertex.
    'Therefore we could create the first vertex with the following line.
    '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.
    Call DX.CreateD3DLVertex(0, -5, 10, DX.CreateColorRGBA(0, 0, 1, 1), 1, 0, 0, LVertexList(1))
    Call DX.CreateD3DLVertex(10, -5, 0, DX.CreateColorRGBA(0, 1, 0, 1), 1, 0, 0, LVertexList(2))
    Call DX.CreateD3DLVertex(0, -5, -10, DX.CreateColorRGBA(1, 1, 0, 1), 1, 0, 0, LVertexList(3))
    Call DX.CreateD3DLVertex(-10, -5, 0, DX.CreateColorRGBA(1, 0, 1, 1), 1, 0, 0, LVertexList(4))
  'We need to copy the first base vertex to make the last side of the pyramid.
    Call DX.CreateD3DLVertex(0, -5, 10, DX.CreateColorRGBA(0, 0, 1, 1), 1, 0, 0, LVertexList(5))
End Sub

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 type.

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.


Main Loop

Now lets take a look at our Main_Loop.

Public Sub Main_Loop()
    Dim stepval As Integer      'The angle to rotate by
    Dim matWorld As D3DMATRIX   'Stores the world matrix, change this to move,
                                'scale and rotate objects.

    Dim tmpMatrix As D3DMATRIX  'Used in world matrix calculations.
    
    'Start our loop, press the 'Esc' key to exit
    Do Until bRunning = False
    
        
         'decrease stepval by 1 to rotate the rectangle clockwise
        'If stepval is less than 0 degrees then make stepval = 360 degrees

        stepval = stepval - 1
        If stepval <= 0 Then stepval = 360
               
               
         '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

        DX.RotateYMatrix matWorld, stepval * Rad
        
         '===================================================
        'If you want to rotate around Y axis and Z axis then
        'include the next 2 lines aswell.
        
        'DX.RotateZMatrix tmpMatrix, stepval * Rad
        'DX.MatrixMultiply matWorld, matWorld, tmpMatrix
        '===================================================
        
        
        'Transform the world

        Device.SetTransform D3DTRANSFORMSTATE_WORLD, matWorld


        'Clear the device AFTER all the changes to any vertices, and BEFORE
        'calling Device.BeginScene and any bltting.

        Call Clear_Device
        
         
        'Begin the scene, must do this after Calling Clear_Device, and before
        'calling Blt3D()

        Device.BeginScene
            
             
            'Render the pyramid to the device (backbuffer), its a triangle fan.

            Call Device.DrawPrimitive(D3DPT_TRIANGLEFAN, D3DFVF_LVERTEX, LVertexList(0), 6, D3DDP_DEFAULT)
            
            
        'Call EndScene when finished using D3D routines
        Device.EndScene
                    
        
        'Flip the primary surface
        Primary.Flip Nothing, DDFLIP_WAIT
        
        'Let windows do its stuff
        DoEvents
    Loop
End Sub

Once again we are going to use stepval as our angle (in degrees) that our object should be rotated by.

DX.RotateYMatrix matWorld, 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 into radians).

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 another tutorial.

Now we use Device.SetTransform D3DTRANSFORMSTATE_WORLD, matWorld to transform the world matrix using our matrix matWorld.

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).


Conclusions

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.

Carl Warwick - Freeride Designs

DirectX 4 VB 2000 Jack Hoxley. All rights reserved.
Reproduction of this site and it's contents, in whole or in part, is prohibited,
except where explicitly stated otherwise.
Design by Mateo
Contact Webmaster