Environment: The code was developed under win 2k but has been tested on windows 98 with the DirectX 7 SDK
The aim of this project is to provide a simple framework to get people started with 3D programming. This project only sets out to initialise DirectX and place the developer in the position where they can concentrate on what they want to do and not on messing about with Microsoft’s DirectX code.
The reason behind this is because I am not a 3D graphics expert but I do have an interest in 3D graphics and for this reason a few years ago I started to dabble. I originally started with DirectX 6 and although I found it hard and unclear of how I was to get what I wanted to do working I eventually managed to get some code written using the framework library. Although the only way I could figure out how to get my code to start up full screen at the time was by including the framework library and then editing it until I got the result I wanted.
Then just as I’d got a test of my idea working DirectX 7 came out. At this point in time I was already thinking that it was going to be a good two years work to finish this project even without changing it to DirectX 7 and a day job. Still I installed DirectX 7 and hit compile and watched in dismay as the compiler generated more errors than it was prepared to be reasonably be expected to put up with and turned itself off in disgust. Never the less I started fixing bugs and got more and more fed up with the whole thing, thus I concentrated on my day job. Then came DirectX 8, a new job, and the thought that I might as well have a look at it. I mean what harm could it do?
The following code was compiled with the DirectX 7 library and uses the Microsoft provided framework classes. It was a deliberate decision to use these classes as although they can sometimes complicate what should be the simplest of operations they do set everything up with very little code which is essential for this project. As I have now come to expect with DirectX, this code will not compile under DirectX 8. This is due to the fact that Microsoft have removed the old vertex types, requiring the programmer to define their own custom vertices and pass a description of the vertices being used to the called function as a define. And this is where the crux of the confusion lies when approaching DirectX for the first time. Most of the code that is available on the web deals with older versions of DirectX which simply need rewriting in order to work with the later versions.
The only solution then would seem to be to turn to the literature out there but unfortunately this can just make the issue even more confusing as the DirectX SDK includes a mode called retained mode which runs in software mode which means that fancy graphics card you just paid two months wages for is getting completely ignored. But it does have one bonus and that is it is easy to use. So a lot of books in the past have concentrated purely on this. Since development on retained mode was dropped ( in DirectX 6 I believe ) the situation has improved but then the problem remains that explaining just what it takes to get everything running seems to mean that the authors get very little time to talk about developing 3D programs, instead being put in a position where if the book is to be coherent they must just explain the internal workings of the DirectX SDK.
The provided code will not have the beginner developing full 3D programs in a week but it will give them a place to start. As a bonus the code also includes a cut and paste port of the Microsoft drawprimitive example that comes with the SDK to show that everything works as expected.
The code starts by inheriting from the framework library main class and simply adds a few get and set functions:
class CNew3dApp : public CD3DApplication
The aim of this class is to override certain function in the base class and provide new implementations of some of the virtual functions. It also provides a few get and set functions to make life a bit easier.
The functions that are overridden are:
/// called first
virtual HRESULT OneTimeSceneInit();
/// called next to initialise app objects
virtual HRESULT InitDeviceObjects();
virtual HRESULT DeleteDeviceObjects();
/// render the scene
virtual HRESULT Render();
/// move the frame
virtual HRESULT FrameMove( FLOAT fMove );
virtual HRESULT RestoreSurfaces();
virtual HRESULT FinalCleanup();
These are all pretty obvious; however, I’ll just give a brief description of each here.
The virtual function OneTimeSceneInit() Is the first function to be called that the code accesses. This is to allow the code to set up the objects that will be required in the program. This function is used by the program to switch on, none major options in this case turning on the Stats function which will place a screen size read out and the frame rate int the top left hand corner of the screen.
The virtual function InitDeviceObjects is where the main objects for the code are set up. This code is re-entrant so if the code is only required to do something once then it is necessary to make sure yourself that it is only done once. I know it seems a strange thing to say when the last function was called OneTimeSceneInit but that function should not be used to change and major aspects such as the screen which is what I am doing here as the internal DirectX structures aren’t setup properly yet.
if( !bInitialScreen )
bInitialScreen = true;
if( FAILED( SetScreenSize( 800, 600 ) ) )
The SetScreenSize function is my only original function in this code. It’s purpose is to allow the program to set the screen to a size determined by the programmer, in this case 800 by 600. There is also the option to retain the windowed mode of most of the demo programs by adding a third boolean parameter which defaults to false and will the draw the application in fullscreen mode. One word of warning if you want to play about with the screen sizes with this code is that it will only draw modes that your video card supports. If your video card doesn’t support the selected mode you will end up with just a blank screen and have to shut down the app and change the resolution in order to get it to work properly.
Here’s the SetSreenSize code,
HRESULT CNew3dApp::SetScreenSize( int nHoriz,
DWORD dwBitsPerPixel )
HRESULT hResult = S_OK;
int nWidth, nHeight;
for( int i=0; i<( int )m_pDeviceInfo->dwNumModes; i++ )
nWidth = ( int )m_pDeviceInfo->pddsdModes[ i ].dwWidth;
nHeight = ( int )m_pDeviceInfo->pddsdModes[ i ].dwHeight;
if( nWidth == nHoriz && nHeight == nVert )
/// check that it’s the right pixel count
if( dwBitsPerPixel ==
m_pDeviceInfo->pddsdModes[ i ].ddpfPixelFormat.dwRGBBitCount)
&m_pDeviceInfo->pddsdModes[ i ],
sizeof( DDSURFACEDESC2 ) );
m_pDeviceInfo->bWindowed = bWindowed;
m_pDeviceInfo->dwCurrentMode = i;
DWORD dwSavedStyle = GetWindowLong( m_hWnd, GWL_STYLE );
GetWindowRect( m_hWnd, &rcSaved );
if( bWindowed != m_pDeviceInfo->bWindowed )
/// never know someone might actually want to
/// draw a window
if( m_pDeviceInfo->bWindowed )
SetWindowLong( m_hWnd, GWL_STYLE, dwSavedStyle );
SetWindowPos( m_hWnd, HWND_NOTOPMOST,
( rcSaved.right – rcSaved.left ),
( rcSaved.bottom – rcSaved.top ),
dwSavedStyle = GetWindowLong( m_hWnd, GWL_STYLE );
GetWindowRect( m_hWnd, &rcSaved );
SetWindowLong( m_hWnd, GWL_STYLE,
hResult = Initialize3DEnvironment();
if( FAILED( hResult ) )
SendMessage( m_hWnd, WM_CLOSE, 0, 0 );
i = ( int )m_pDeviceInfo->dwNumModes;
SetLastResult( hResult );
The SetScreenSize cycles through the modes available and checks the width and height passed to the function against the width and height of the current mode. It should be noted here for the beginner that the modes contained in the m_pDeviceInfo structure are worked out by the DirectX framework code when on initialization the DirectX code queries the video card to see what modes it supports. Once the code has found the right width and height it then checks that the bit depth is equal to the bit depth passed in the dwBitsPerPixel parameter. If this is not set to one of the accepted values ( 4, 8., 16, 24, or 32 ) or the chosen value is not supported by the video card then the program will run in the default mode. If the dwBitsPerPixel is supported for that width and height the code will copy the selected mode to the m_pDeviceInfo->ddsdFullscreenMode structure and set the screen parameters before reinitialising the drawing mode to that contained in the m_pDeviceInfo->ddsdFullscreenMode structure.
The rest of the InitDeviceObjects code contains code from the Microsoft’s DrawPrimitive Example which is delimited by:
/// CODE FOR MICROSOFT’S DRAWPRIMITIVE EXAMPLE
/// END CODE FOR MICROSOFT’S DRAWPRIMITIVE EXAMPLE
For information on this code see Microsoft’s documentation. In order to use the code as a template for your own projects simply remove the code between the lines designated as part of the Microsoft DrawPrimitve example.
The virtual function DeleteDeviceObjects is for removing any memory structures that the code has established in the InitDeviceObjects function and in this case remains empty. It should be noted that this code is also re-entrant so make sure that all memory structures are checked as valid before they are deleted.
The virtual function Render is where the drawing takes place although in a large project this is likely to contain code that decides what other functions are to be called. The render function contains a certain amount of standard code this is:
hResult = m_pd3dDevice->BeginScene();
if( SUCCEEDED( hResult ) )
/// draw items directly or call separate drawing functions
/// End the scene
hResult = m_pd3dDevice->EndScene();
The BeginScene function notifies the DirectX libraries that the code is about to draw/redraw the scene that will be rendered to the screen by DirectX. This allows the DirectX libraries to ensure that everything is internally ready for the scene to be drawn. The EndScene function merely notifies the DirectX libraries that the scene has been drawn. The example code draws three objects to the screen and is taken directly from the Microsoft DrawPrimitive example.
The virtual function FrameMove is the animation part of the code the objects are drawn by the render function but this is where rotation and movement of the objects is plotted. If you look at the code you can see that the initial positions for the three objects on the screen are set in the InitDeviceObjects function they are then rendered at these positions in the render function. Note though that the object positions do not have to remain static they can be changed in the render function, as long as its before the call to the actual drawing code in this case DrawPrimitive and DrawIndexedPrimitive.
The virtual function RestoreSurfaces is used to restore the buffers for DirectX if any of them have been lost. This can happen when running an application in a window and then giving another application the primary focus and the virtual function FinalCleanup is self explanatory in that this is where you delete all the memory that you have allocated and release any com pointers before the application closes down.
BUILDING THE SAMPLE
In a perfect world you should be able to build the sample directly from the code provided but when I was doing the final testing for this project I installed a new version of DirectX Seven and tried to build it and found that I needed to rebuild the Framework library and copy the built library over the one provided. The project for the library can be found in the DirectX Seven Samples Immediate Mode Directory in D3DFrame folder. Simply load the project into developer studio and rebuild all. Not forgetting to copy over the original in the Dim\lib folder.
If you are using the project as a template remove all the code marker between the lines
/// CODE FOR MICROSOFT’S DRAWPRIMITIVE EXAMPLE
/// END CODE FOR MICROSOFT DRAWPRIMITIVE EXAMPLE
And copy the project into a new folder. It is unlikely, however, that anyone would want to call all there 3D applications New3dApp so once the project is open go to project\settings and in the link options sheet type what you want your executable to build as.
There you have it there should be enough to start delving into the fun parts of DirectX and start drawing things to the screen in fullscreen mode without getting swamped by all the technical details of how to set it up properly. Unfortunately this project, as stated above, will only work with DirectX Seven. I’ll write the code and an article for getting started with DirectX 8 soon.