DirectX 8- DirectGraphics

 

DirectX8 is a frightening thing for the amateur game designer, mostly because, unlike its older brother DX7, it doesn’t have a 2D API. Even people who are trying to move straight from DirectX7 are having troubles. I’m no exception, so I’m documenting my attempt to learn the 3D API and how to use it to it’s full potential, and I’m going to show you all in the form of a series of tutorials.

 

So where do you start your new DirectGraphics project? Well, as stupid as it sounds, from the beginning.

 

DirectGraphics draws its images through a device, which is comparable to a car driver… the driver can drive pretty much any car… even though almost every car may have a small difference. The DirectGraphics device can use pretty much every 3D video card, but they all have their minor differences.

 

You haven’t closed the window yet? Shock! Okay, well first of all, let’s make 5 declarations which will control the objects and running of the program. Put these wherever you want, but for the demo app, they’re in a form.

 

Dim objDX as DirectX8

Dim objDG as Direct3D8

Dim devDG as Direct3Ddevice8

 

Dim DGInited as Boolean

Dim ProgramExiting as Boolean

 

objDX is the object that is the root of all DirectX8 commands, DirectGraphics will be created from this object later and NO DirectX8 projects can be made without it, it’d be like trying to use VB without windows installed on your computer (I’m under the impression the VB6 CD isn’t bootable…)

objDG is the object that controls the operation of DirectGraphics. It’s extensively called Direct3D, but I’ve adopted calling it DirectGraphics because that’s what Microsoft call it in their SDK. This object will be responsible for creating the device.

devDG is a DirectGraphics device, which is responsible for the rendering of any image.

 

DGInited helps us keep a track of whether or not the initialisation of DirectGraphics was successful

ProgramExiting will let our rendering loop stop when the user closes the program.

 

Okay then! That’s all cute and furry?

Open up the form’s code, and select Form_Load from the methods

 

Now then, we’ll start by making it display the form because it won’t have a chance to until the rendering loop starts…

 

Private Sub Form_Load()

Me.Show

Me.Caption = “Create Device: Initialising”

 

Yawn hehe, you didn’t need to know that did you?

 

Okay, well, declaring objects is one thing, but they’re useless until you actually set them to something, So we’ll start at the top and work down, lets first set an new instance of DirectX8 (We don’t want it using another program’s DirectX do we now… that could get messy…)

 

            Set objDX = New DirectX8

 

            Okay, now let’s leave the Form_Load for a little bit and do something a little more interesting, ie, our initialisation function J

 

            As with every good function, let’s start with our declarations and some code to trap errors

 

           

Function D3Dinit() as Boolean

            Dim udtCurrentMode as D3DDISPLAYMODE

            Dim udtPParam as D3DPRESENT_PARAMETERS

 

            On Error GoTo ErrorCheck

 

            udtCurrentMode will display all the info regarding the screen’s current display mode, the bit depth, the width and height, etc, etc, etc… We’ll need this because it’s got the information we need to make the backbuffer (somewhere to draw that won’t get seen until it’s finished, this stops flickering)

            udtPParam is where we’ll provide DirectX the data it needs to display the graphics to the user

 

            The last line will cause the program to fail if any part bombs out.

 

Set objDG = objDX.Direct3DCreate

 

Call objDG.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, udtCurrentMode)

 

With udtPParam

.Windowed = 1

.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC

.BackBufferFormat = udtCurrentMode.Format

End With

   

Set devDG = objDG.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, udtPParam)

If devDG Is Nothing Then GoTo ErrorCheck

 

D3DInit = True

Exit Function

 

            okay, now the first thing we need to do is create the DirectGraphics object, because it won’t really know how to behave if it’s not initialised. That’s what the first line does.

 

            The second line calls the method .GetAdapterDisplayMode that gets all the information available for a particular device. The only reason we’re getting this is so we can get the pixel format, (how many bits of red, green and blue) so that the backbuffer will match. You wouldn’t need to call this on a full-screen application because you’ll be specifying the pixel format yourself.

            The method is thus:

 

object.GetAdapterDisplayMode(Adapter As Long, Mode As D3DDISPLAYMODE)

object is an object which relates to a Direct3D8 object (objDG in this case)

Adapter is a long value, which identifies the video card to be analysed, unless you’re on a two monitor configuration, it’s recommended to use the primary [D3DADAPTER_DEFAULT]

Mode is a user defined type which will store the retrieved information (udtCurrentMode in this case)

 

The next five lines fill the user defined type udtPParams with the information that will be required to fulfil a .present request (the command that displays the rendered graphic)

 

.Windowed will tell the graphics device that the program is windowed where 1 = true, and 0 = false. I don’t recommend setting this to True, because I have had troubles (True is actually –1)

.SwapEffect tells the device the way in which to display the information. It will either copy the information from the backbuffer to the primary buffer [D3DSWAPEFFECT_COPY], it will copy it and keep it in sync with the video card to get rid of flicker [D3DSWAPEFFECT_COPY_VSYNC] (Your usual choice for windowed apps), or you can get it to just alter the location of the video memory [D3DSWAPEFFECT_FLIP], called “Flipping” and is theoretically much faster (Assuming the backbuffer and primary buffer are both in the video card’s memory). There is another option [D3DSWAPEFFECT_DISCARD] but I’m still translating that one from Microsoft into English.

.BackBufferFormat will tell the device what kind of backbuffer to create. The backbuffer MUST be the same format as the primary surface (The current display adapter’s mode)

 

The next line actually creates the device that will render the information. This is what the actual command is:

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

object is an object which relates to a Direct3DDevice8 object (devDG in this case)

Adapter is a long value, which identifies the video card to be analysed, unless you’re on a two-monitor configuration, it’s recommended to use the primary [D3DADAPTER_DEFAULT]

DeviceType tells DirectGraphics what kind of device you would like (but like is not necessarily what you’ll get…) you have three choices, [D3DDEVTYPE_HAL] or Hardware acceleration this will use your video card to do all that it can, it will try to emulate in software whatever your card can’t do in hardware. [D3DDEVTYPE_REF] or The Reference Rasterizer, More for debugging purposes, the Reference Rasterizer supports EVERYTHING, and because of that it’s SLOW as all hell. Only use the Rasterizer when you’re testing to see what something will look like if your card doesn’t support it. Lastly, [D3DDEVTYPE_SW] The software rendering device, the capabilities depend on the software device that has been registered, but regardless, it will almost always be faster than the Rasterizer, though not as good looking. At least it will be playable in a game though J.

hFocusWindow is simply the window handle of what’s displaying the graphics; this is usually a form or a picturebox. Just simply pass the object’s hWnd here.

BehaviorFlags tells how the device will behave. There must be one of three options specified here: [D3DCREATE_SOFTWARE_VERTEXPROCESSING] the only one you can call if you don’t enumerate devices, the device will calculate vertex transformation, scaling and rotation in software, (We’ll get into vertices later). [D3DCREATE_HARDWARE_VERTEXPROCESSING] will dramatically speed up your application by performing all vertex calculations in hardware. [D3DCREATE_MIXED_MODE_VERTEXPROCESSING] Microsoft is not very clear about this one, it doesn’t say whether the it will fall back on software if hardware acceleration is not possible or what… Until I research more on this, I don’t recommend newbies use this flag.

PresentationParameters these are what you created a few lines ago, and tell the adapter how it will go about displaying the information to the user. The parameters sent to this control the surfaces created.

 

Sigh coor, that one was a mouthful. Did I confuse anybody? I don’t know how to make it any simpler than that… sorry guys.

The next line just simply says, if you couldn’t create the device, drop out and give a Macintosh error (you know, one of those ones that isn’t very descriptive that nobody can figure out)

And if the device was created, everything was successful, so set the function to true (Success) and exit.

 

Lastly, we’ll do the very short and undescriptive error checking

 

ErrorCheck:

D3DInit = False

End Function

 

            This one is pretty simple, if it reaches this, then set the function to return False (Failure) and exit the function

 

            Okay! That’s the initialisation all over and done with! Now we’ll head back to the Form_Load function and pick up where we left off okay?

 

            Set objDX = New DirectX8 ‘this is where we left

 

            DGInited = D3Dinit()

            If DGInited = False then

                        MsgBox (“Critical Failure initialising DirectGraphics”)

                        End

            End If

           

            Okay, first of all, we’ll call the function we just created and store the result in DGInited.

            If DGInited is false, then something went wrong in the initialisation. The correct way of doing this is to actually declare D3Dinit() as a long, not a Boolean, then you can spit back some useful information to the user. Unfortunately, I’m writing this tutorial very late at night, and I couldn’t be bothered putting in the correct error checking (But hey… this is the basic code and should work on all computers… if this doesn’t work, you must have mistyped something.

            Once you’ve given them their Macintosh error, end the program because it just won’t work.

 

            Now let’s finish off the Form_Load subroutine

 

Me.Caption = "Create Device"

   

Do

Call devDG.Clear(0, ByVal 0, D3DCLEAR_TARGET, &HFF&, 1#, 0)

 

devDG.BeginScene

devDG.EndScene

       

Call devDG.Present(ByVal 0, ByVal 0, 0, ByVal 0)

       

DoEvents

Loop Until ProgramExiting = True

End

End Sub

 

            the first line is pretty explanatory, we’ve finished initialising, so lets not delude the user.

 

            Next, lets set up a loop, and the first thing we’ll do is clear the viewport (the section of screen the user can see)

            We’ll use the devDG.Clear method. This will clear a number of rectangles which you can put in an array. However, if no rectangles are specified, it will clear the entire viewport. The .Clear method looks a little something like this:

 

            object.Clear(Count As Long, ClearD3DRect As Any, Flags As CONST_D3DCLEARFLAGS, Colour As Long, Z As Single, Stencil As Long)

            object is an object which relates to a Direct3DDevice8 object (devDG in this case)

Count the number of rectangles that are to be cleared. If there are 10 triangles (0 to 9) then you’d pass 9 etc etc.

ClearD3Drect an array of rectangles to be cleared. DirectX says it can be any, but it’s really an array of D3DRECTs.

Flags tells the clear command what you are clearing. It will be any of these three constants: [D3DCLEAR_TARGET] The target is the viewing area. [D3DCLEAR_ZBUFFER] which will clear the depth buffer (We’ll discuss that later). [D3DCLEAR_STENCIL] will clear the stencil buffer (again, this will be discussed later). It’s important to note that you shouldn’t clear anything that doesn’t exist. If you don’t have a stencil buffer DON’T clear it. You’ll get a nice error for your efforts.

Colour is a 32-bit colour, Alpha, Red, Green, and Blue. It’s very easy to create colours using DirectGraphics, especially in Hexadecimal, just remember to put it in the form &HAARRGGBB, and if you want your colour to be solid visible, use AA as &HFF otherwise you’ll be trying to figure it out for ages!

Z is the Z-Buffer that you attached to the surface.

Stencil is the stencil buffer that you attached to the surface.

 

Okay, on top of all this, there is one VERY important thing to remember. All of these values are required, so how do you call the command if you don’t have a Z-Buffer or a Stencil buffer. Or more practically, what if you’re not clearing any rectangles (the whole viewpoint will be cleared)

Well… for each of these, instead of passing an object, you’d pass ByVal 0. This basically tells DirectGraphics that there is no array or object. So when you pass 0 as the number of rectangles, the ClearD3DRect should be ByVal 0. Understand? (Thanks to lucky for helping me figure that one out)

 

Okay, next we see two commands, devDG.BeginScene and devDG.EndScene these two commands are issued when you’re about to start rendering polygons to the device. You can’t do it outside of these methods. Unfortunately, this tutorial is all about creating the device, not using it. So there is nothing between there for now.

 

The last thing we need to do is to .Present the information to the user. Easy? I doubt it… this is DirectGraphics we’re talking about… So here we have the description of the devDG.Present command:

 

object.Present(SourceRect As Any, DestRect As Any, DestWindowOverride As Long, DirtyRegion As Any)

            object is an object which relates to a Direct3DDevice8 object (devDG in this case)

            SourceRect is a rectangle which defines which section of the backbuffer to grab from

            DestRect is a similar rectangle which defines which section of the primary buffer it will be pasted to.

            DestWindowOverride you can pass a new hWnd here to change where the graphic will be rendered. Just pass 0 if you don’t want to override the current window.

            DirtyRegion isn’t used, Microsoft tells us that it should always be set to ByVal 0. It must be one of those “C++ thingys”

 

            Much like the .Clear command, anything which isn’t specified, should be set to ByVal 0, if you want to swap the entire backbuffer to the front, you have to pass ByVal 0 to both the source and destination rectangles.

 

            Next we’ll let the user click on the close button if they want to close (as we’re sure they will, I don’t think anybody really wants to stare at a blue screen… there are plenty of other ways that can be much more interesting to them…

 

            Now we’ll end the loop, and if the user has clicked on the close button, we’ll exit the loop and end the program.

 

            Phew that’s it for the rendering loop. Nothing to it huh? At least, nothing to it when you understand it all…

            Now I do think there is something I’ve forgotten…. Oh yeah… what’s going to happen when the user tries to close the program… the variable still won’t be set to close… let’s fix that and some potential memory leaks…

 

Private Sub Form_Unload(Cancel As Integer)

Set devDG = Nothing

Set objDG = Nothing

ProgramExiting = True

End Sub

 

            All we’re simply doing is destroying the two DirectGraphics components we created and setting our ProgramExiting Boolean to true.

            It’s that simple.

 

            Go on… run it… I dare you.

            Voila, a beautiful DirectGraphics blue screen. (Or a GDI Blue screen of death, depending on whether or not you were paying attention hehe)

 

            Okay, so please don’t hit me. You’ve just wasted how long trying to create a blue screen? Yeah I know… But there is a lot that is hard to understand, so I’m trying to make it idiot proof (My apologies to all the idiots that might be out there reading this)

 

            Well, that’s it. Download source here. I’m going start researching now how to actually DRAW a triangle next. This ought to be interesting…

 

 

                                                Written By

                                                            Steven Blom AKA

                                                            LynxofCP (Lynx of Central Programming)

            If there is any information here guys that you know that I don’t, or if you find a better way to explaining something please don’t hesitate to E-Mail me at CodeWarrior@octa4.net.au