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