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

DirectX Graphics: Getting Started
Author : Jack Hoxley
Written : 30th November 2000
Contact : [Email]
Download : Graph_01.Zip [8 Kb]


Contents of this lesson:
1. Introduction
2. Getting Started
3. Rendering
4. The Main Loop
5. Cleaning Up
6. Extending this sample to use fullscreen mode.


1. Introduction

Welcome to the first lesson of many on DirectX8-Graphics. There are three likely reasons why you're reading this: You're completely new to the world of DirectX and VB, you're interested in upgrading from DirectX 7, you're interested in upgrading from DirectX7, have looked in the DirectX 8 SDK and gotten very confused. Either way you're here.

The DirectX 8 graphics implementation is quite different to what you may well be used to if you're a veteran of earlier versions; the main thing you'll have noticed is that there is no DirectDraw interfaces left. In DirectX 7 it was possible to use 3D (and the hardware accelerator) to generate 2D graphics; it wasn't easy - but it was possible; In DirectX 8 this is the only way to generate 2D graphics. Dont worry about this part if you're new to DirectX completely.

DirectX 8 offers us many incredible features, sadly alot of them are extremely complicated and unlikely to be used by anyone except a professional. Things such as the microprogrammable architecture for per-pixel processing and vertex processing will make games more lifelike; Mesh skinning will make the representations of players more realistic and various other features. This site will try to explain as many of these as is possible.

If you're a seasoned DirectX 7 programmer you can jump straight to part 2; for those of you not familiar with DirectX - a brief explanation follows. DirectX is a collection of classes and interfaces that allow low-level access to hardware. They get their speed almost entirely from the fact that they are very simple and very "thin". A call to DirectX will get to the hardware much faster than if you use the traditional windows API calls, which go through several stages before appearing to do anything. DirectX also supports almost every feature imaginable from the current crop of 3D cards, sound cards, internet connection and input devices; that is until the next batch are released... From a programmers point of view we create an instance of a DirectX component, be it DirectX in general, Direct3D, DirectSound, DirectMusic. Once our application has access to this we can start playing around, initially we'll set the hardware up - in graphics we'll set the display mode, any rendering options, load textures and geometry. Once we're done with that part we'll enter a tight loop (in most DirectX apps) where we'll update anything that needs updating and usually render the next frame onto the screen - this is the basis of a frame rate; a frame rate of 70 indicates that this loop will be processed 70 times a second...

The rest you'll learn as you go along. Hopefully.


2. Getting Started

The first part of our DirectX graphics series is to learn how to setup a basic framework. Should you keep with DirectX the code you learn here will be used time and time again, eventually you'll probably be able to recite it in your sleep (depending on what sort of person you are!). If you dont follow this code now, you'll be lost later on; go over and over this until you understand it inside out - later lessons will assume that you can set up a basic framework.

2a. Attach DirectX8 Library and start our project.
Start a new project in Visual Basic, Standard EXE will do fine. You should see that a single form is added - This should be the norm for you, as it's unwise to venture into DirectX if you have little VB experience. Go to the project menu and select "references". Scroll down until you find an entry in the list called "DirectX 8 for Visual Basic Type Library" and check the box next to it - then click okay. Save you're project if you want. This project is now fully capable of utilising DirectX 8. Should you happen to have any problems with the above code; there are several solutions:
1. Have you got DirectX 8 installed? If not, the entry wont appear in the list.
2. Have you got Visual Basic 5 or above? DirectX uses the Component Object Model (COM) which was only implemented in Visual Basic 5 and onwards.
3. You have both of the above, and it still doesn't appear. Click on the "Browse" button and point it towards the file C:\Windows\System\dx8vb.dll and click okay - and it should be fine.
4. The above instructions are fine for VB 5 and 6, but at time of writing VB 7 (or VB.Net) has not been released - so slight changes in the interface may well apply...

2b. The Variables
This part goes in the Declarations section of a form:

'//The variables Required
Dim Dx as DirectX8 'The master Object, everything comes from here
Dim D3D as Direct3D8 'This controls all things 3D
Dim D3DDevice as Direct3DDevice8 'This actually represents the hardware doing the rendering
Dim bRunning as boolean 'Controls whether the program is running or not...

'//These aren't really required - they'll just show us what the frame rate is...
Private Declare Function GetTickCount Lib "kernel32" () As Long '//This is used to get the frame rate.
Dim LastTimeCheckFPS As Long '//When did we last check the frame rate?
Dim FramesDrawn As Long '//How many frames have been drawn
Dim FrameRate As Long '//What the current frame rate is.....

2c. The Initialisation
After this part you'll need to initialise your objects, and set some basic parameters. Nothing complicated yet, but for now we'll stick to using windowed mode (Not fullscreen, as used by most games).

'// Initialise : This procedure kick starts the whole process.
'// It'll return true for success, false if there was an error.
Public Function Initialise() as Boolean
On Error Goto ErrHandler:

Dim DispMode as D3DDISPLAYMODE '//Describes our Display Mode
Dim D3DWindow as D3DPRESENT_PARAMETERS '//Describes our Viewport

Set Dx = New DirectX8 '//Create our Master Object
Set D3D = Dx.Direct3DCreate() '//Make our Master Object create the Direct3D Interface

D3D.GetAdapterDisplayMode D3DADAPTER_DEFAULT, DispMode '//Retrieve the current display Mode

D3DWindow.Windowed = 1 '//Tell it we're using Windowed Mode
D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC '//We'll refresh when the monitor does
D3DWindow.BackBufferFormat = DispMode.Format '//We'll use the format we just retrieved...

'//This line will be explained in detail in a minute...
Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, FrmMain.Hwnd, _
                                                     _ D3DCREATE_SOFTWARE_VERTEXPROCESSING, _
                                                     _ D3DWindow)

Initialise = True '//We succeeded
Exit Function
ErrHandler:
'//We failed; for now we wont worry about why.
Initialise = False
End Function

This function should, if all goes according to plan, create all the objects required - ready for use by us. But there are several things that need to be explained, and several things that could already go wrong...

The main thing that can go wrong is the CreateDevice call. There are several parameters in this call that are hardware dependant. Lets analyse this line:
The prototype for this function looks like this:

object.CreateDevice(Adapter As Long, DeviceType As CONST_D3DDEVTYPE, hFocusWindow As Long, BehaviorFlags As Long, PresentationParameters As D3DPRESENT_PARAMETERS) As Direct3DDevice8

Adapter : This is either primary or secondary. D3DADAPTER_DEFAULT always represents the primary adapter; we'll cover the usage of secondary adapters in a later lesson. Basically all they do is switch between a primary video card (a standard 2D/3D card) or a secondary card (such as the 3D-only voodoo 1's and 2's)
DeviceType : This basically allows you to choose between the HAL (Hardware Accelerator) and the reference rasterizer (A debugging tool). There is a third option, software rendering, which is designed to allow plugin support for custom renderers; the DirectX DDK (Driver Development Kit) has information on doing this; but if you can write your own 3D renderer you're unlikely to be using VB... :) Specify either D3DDEVTYPE_HAL or D3DDEVTYPE_REF. Bare in mind that if there is no support for the HAL available this call will fail and you wont create a device.
hFocusWindow : This is just a value that DirectX can use to track your applications status. This value comes from the Form.hWnd value; the value specified must be a valid, created form with nothing fancy about it - no circular forms or MDI forms please :)
BehaviorFlags : This just sets how the Direct3D engine processes textures, vertices, lighting and so on. The best option to use here would be D3DCREATE_PUREDEVICE - but very few graphics cards will support this (even the relatively new TnL GeForce 256 cards), using this option will mean that the 3D card does almost everything - transformation, shading, lighting, texturing and rasterization. If you can't use this on you're hardware the next best thing will be D3DCREATE_HARDWARE_VERTEXPROCESSING - this uses hardware acceleration as much as possible; most recent 3D cards will support this. Failing this you could try D3DCREATE_MIXED_VERTEXPROCESSING which will use hardware when possible, but if the hardware can't handle it then the software components will kick in. Last of all is the plain software rasterizer; should there be no 3D hardware available this is likely to be the only option. It's almost always very slow, and not a nice thing to use - should you want to : D3DCREATE_SOFTWARE_VERTEXPROCESSING.
PresentationParameters : This depicts what display mode you want to use. Pass the current display mode (as we have earlier) and you can use windowed mode; alter these settings (shown later) and you can enter fullscreen mode.

As you should now understand, there are several hardware dependant variables; to solve this problem we'll use a process called enumeration to work out what the host computer can do. That's in a later lesson though; this one's just the beginning.


3. Rendering

This will eventually become the main part of you're program. The code you write here will be executed as many times as possible in a loop; unclean code will mean poor performance, clean and fast code will result in blistering frame rates and smooth gameplay. After you've got the initialisation process sorted out and setup how you require it your work will center mostly on this section.

Rendering is almost always started in one procedure; other procedures may well be called from this procedure, but it will tend to be mostly in one procedure. It'll almost always follow the same pattern:

1. Update any changes to vertices, Camereras, textures etc...
2. Clear the screen - removing the last frames work
3. Draw the new frame - this is the lengthy part.
4. Update any final variables
5. Copy the rendered image to the screen (Primary.Flip for veteran DirectX7 programmers)

So, for this simple lesson we'll create a basic framework for this procedure:

Public Sub Render()
'//1. We need to clear the render device before we can draw anything
'       This must always happen before you start rendering stuff...
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, &HCCCCFF, 1#, 0
'the hexidecimal value in the middle is the same as when you're using colours in HTML - if you're familiar
'with that.

'//2. Next we would render everything. This lesson doesn't do this, but if it did it'd look something
'       like this:

D3DDevice.BeginScene
    'All rendering calls go between these two lines
D3DDevice.EndScene

'//3. Update the frame to the screen...
'       This is the same as the Primary.Flip method as used in DirectX 7
'       These values below should work for almost all cases...
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub

This code excerpt is very simple, it doesn't draw anything and it doesn't update any variables. But you'll start to see how this changes in later lessons.

One thing to be mentioned here is the use of "ByVal 0" for some of the parameters. When you type "D3DDevice.Present " and the tooltip appears showing the parameters you'll notice that it SourceRect, DestRect and DirtyRegion are all defined as "Any". This means that you could, theoretically, pass any type of data as the parameter. If we just put 0 in as the parameter visual basic would interpret it as meaning "Nothing" - which is not what we want; we actually want the value 0 to be passed. If we stick the ByVal part in visual basic will make sure it goes through as a number, rather than "Nothing".


4. The Main Loop

The main loop is basically a small piece of code that executes a very tight loop; for every loop we do we'll update the graphics, AI, Sound, Physics (or whatever else the game needs to do). The faster this code executes the higher the frame rate - Simple as that. Because of the way a loop operates we can also include the initialisation and termination code in the same procedure. It'll look like this:

Private Sub Form_Load()

Me.Show '//Make sure our window is visible

bRunning = Initialise()
Debug.Print "Device Creation Return Code : ", bRunning 'So you can see what happens...

Do While bRunning
    Render '//Update the frame...
    DoEvents '//Allow windows time to think; otherwise you'll get into a really tight (and bad) loop...
    
    'Calculate the frame rate; how this is done isn't greatly important
    'So dont worry about understanding it yet...
    If GetTickCount - LastTimeCheckFPS >= 1000 Then
        LastTimeCheckFPS = GetTickCount
        FrameRate = FramesDrawn '//Store the frame count
        FramesDrawn = 0 '//Reset the counter
        Me.Caption = "DirectX-Graphics: Lesson 01   {" & FrameRate & "fps}" '//Display it on screen
    End If
    FramesDrawn = FramesDrawn + 1
Loop

'//If we've gotten to this point the loop must have been terminated
'   So we need to clean up after ourselves. This isn't essential, but it'
'   good coding practise.

On Error Resume Next 'If the objects were never created;
'                               (the initialisation failed) we might get an
'                               error when freeing them... which we need to
'                               handle, but as we're closing anyway...
Set D3DDevice = Nothing
Set D3D = Nothing
Set Dx = Nothing
Debug.Print "All Objects Destroyed"

'//Final termination:
Unload Me
End
End Sub

You'll notice that all of this code is in the form load procedure; this'll mean that DirectX is initialised and setup when the application is first loaded, and it'll go straight into the main game loop. One immediate thing to remember is that you MUST include the "Me.Show" procedure as one of the very first lines. In the normal life of a VB application the form isn't displayed until after the Form_Load code is completed - but in our case it'll only be completed when the application closes. With the "DoEvents" line included the form will eventually be shown, but it wont be very tidy.

The main loop structure is based completely on a boolean variable, bRunnning, whilst this is set to true the main loop is executed. As soon as it turns false we'll leave the loop and continue executing the Form_Load code; which, as you can see, will quickly clean up DirectX (explained later) and close the application. You can think of this variable as an off switch - and the way the loop works it'll respond very quickly; at 60 frames per second it should respond in 1/60th of a second to your request.

There are two other things that you need to think about with the main loop in this example. The main one being what order the procedures are called in - which isn't important here (only 1 procedure) but in a full game structure you'll have several procedures processed on every loop. A good example is when you're graphics engine depends on the input from the keyboard - if you call the graphics engine THEN the input engine you're graphics engine will always be 1 frame behind what the user has just done, if you call the input engine first, then the graphics engine everything will be synchronized. Finally, the frame rate calculation. This is quite useful as you can instantly get an idea of what speed your application is running at. All it does is increment a counter on each loop and every second copies it to a more persistant variable and sets the counter back to 0.

The last thing to note about the main loop is the "DoEvents" line. Without this simple call things would go pair shaped very quickly. This line allows windows to "Breath" so to speak; without it almost everything will stop - forms won't appear, mouse/keyboard input wont be registered and setting any properties will be delayed (such as altering the caption in this example). This is particularly bad when you require a key/mouse event to terminate the main loop - if no keyboard and mouse events are registered then you cant terminate the loop, if you cant terminate the loop it just keeps on going - until it crashes (which is likely to happen after a while). For safety just leave the line there - dont think about it, just remember it.


5. Cleaning Up

Cleaning up is the last thing that you shoudl do; although it's not always the end of the world if you forget it's good practise to do it. When cleaning up you should free all the objects in the reverse order that you created them. You've actually already seen the code for cleaning up this lesson's code:

On Error Resume Next
Set D3DDevice = Nothing
Set D3D = Nothing
Set Dx = Nothing

Simple really, to free up an object you just code "Set <Object Name> = Nothing" and it's fine. I've included the on error resume next part because you can sometimes get an error if you set an object to nothing when it's never been created - which will happen in this sample if the initialisation fails.


6. Extending this sample to use fullscreen mode

If you run the sample program at this point you'll realise that it's running in windowed mode (I did tell you as well). Although a lot of games have an option for using windowed mode, by default they'll use fullscreen mode. There are many reasons for this, but it's basically down to look and feel - A game looks more real and feels better if you cant see "My Documents" or the start-bar in the background.

Switching to fullscreen mode isn't very complicated, we just need to re-design our structure slightly:

DispMode.Format = D3DFMT_X8R8G8B8
DispMode.Width = 640
DispMode.Height = 480

D3DWindow.SwapEffect = D3DSWAPEFFECT_FLIP
D3DWindow.BackBufferCount = 1 '//1 backbuffer only
D3DWindow.BackBufferFormat = DispMode.Format 'What we specified earlier
D3DWindow.BackBufferHeight = 480
D3DWindow.BackBufferWidth = 640
D3DWindow.hDeviceWindow = frmMain.hWnd

Not too complicated really; the above code goes in place of the existing "D3DWindow" initialisation code that we were using earlier.

I'll assume that you know what a resolution is (640x480, 1024x768 and so on), so I wont go on about that, but there is one thing that you'll need to pay attention to when using the above code.

The Display mode format - DispMode.Format - as you can see at the top of the code it is set to being "D3DFMT_X8R8G8B8". So what the heck does that mean? Well, all textures and surfaces (backbuffers, primary buffers, depth buffers etc...) are stored in a certain format in memory. You've probably come across bit depths when using resolutions - 8 bit, 16 bit, 24 bit, 32 bit. These tell you how many bits of memory are required for each pixel, there are 8 bits in a byte, therefore 32 bit colour requires 4 bytes of memory for every pixel stored. The higher the bit depth the more memory is required (but the nicer it'll look). So what relevence has this information? the "D3DFMT_X8R8G8B8" flag specifies how this memory is setup - in this case 8888 format = 32 bit colour. There are several other formats that you'll come across a little later on, but right now we'll keep things simple. With the code above you'll need to have a computer that supports 640x480 in 32 bit colour, if you dont then it'll fail. Also, you'll need to bare in mind the actual screen width/height - if you know that the hardware doesn't support the specified resolution then you'll get an error... A later lesson will discuss enumeration - the process of working out what the hardware supports.


You can download the sample code for this tutorial from the top of the page.

Assuming you understood all of this, you're ready to move onto Lesson 02 : Enumerating the display adapters and available display modes.

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