EVIL STICK MAN ENTERPRISES

Game Development 101 – Opening a Window, Connect the Dots, and ROMs

Welcome back, my friends, to the show that never ends. Last month we discussed a slew of online gaming resources – some of which I’d highly suggest bookmarking – and some websites for those players who might be a little less than honest. This month, we’ll cover opening a window in both DirectX and OpenGL (using the OpenGL Utility Toolkit (GLUT)), then we’ll draw some lines and dots to the screen.

Yeah, I know – not very exciting stuff. But you have to walk before you can run, and since there is so much involved with game programming, we have to take it slow. Don’t worry, I’ll have plenty of conceptual jargon thrown in to liven things up (we’ll get into some of the more important concepts, such as the Game Loop and ::shudder:: documentation). If nothing else, you’ll at least be able to draw to the screen at the end of this article.

Before we start, however, I have to touch on something that might be a little unpleasant. You can feel free to use any of the code I put here, but please, give me credit for anything you take verbatim. If you take it and modify it, that’s cool, it’s an original work then (there are only so many ways to do things, anyway), but anything I’ve drawn using the code, as well as any textures or music I offer (those will come in the future) are mine, and while they’re free to use in an educational setting (so long as I’m given credit), you’re going to need written permission from me to use them commercially. I’m not pointing any fingers, but the subject had to be broached by someone (and this way I cover my behind from the start).

Anyway, with the serious business taken care of, let’s get started!

Code Monkey’s Cove

I debated for a while what to start with here, seeing as it is your first real step towards programming for games. After hours of intense deliberation (read: alternating between games of Go and Warcraft III), I decided to start with a little bit of theory. Don’t worry, you won’t find many equations here – those don’t come until we hit 3D graphics. I’m talking theory of the underlying structure of games. The basic form that most game engines run from.

Nearly every game has something in common. Whether they’re as different as Pong and Doom 3, almost every game ever created has the same three basic elements, repeated over and over:

  • Draw – draw the game to the screen
  • Process input – retrieve input from the user
  • Update the screen – use the results of the input to update the game

They may all do it differently, but there it is in three steps – your basic game. This portion of the code is known as the Game Loop. As a result of this method of game programming, regardless of your language your main function, the one that runs it all, will always look very similar to the following:

int main(int argc, char *argv){
  //set the game to start
  quit = false;
  //perform initialization here
  //the game loop
  while(!quit){
   draw();
   getInput();
   update();
  }
 
//perform shutdown tasks here
   return 0; //end
}

Code Example 1 – the main function

The setup and shutdown tasks will differ from language to language and API to API, but the general structure is always the same – do three things, and do them repeatedly. Most games will simply loop forever until some other function finds a creative way to exit, but if you’ve looked at my code you’ve probably noticed that I opted for a different route. I don’t much like the idea of infinite loops, as I ran across one too many in my undergrad programming projects, so I left a global way out. Plus, the idea of a program exiting logically just sounds better to me – if a function decides to end the program, something can easily get forgotten, and things like destructors not getting called will happen (which can lead to memory leaks and other fun stuff your users won’t very much enjoy).

Another thing that you may notice as we go along is that the game loop may appear in different places. In DirectX it’s actually a self-generated loop that plugs into the Windows message queue (more on this later). In OpenGL (using Glut, as we will be), you’ll probably never even see the game loop, as Glut automates the first two steps, leaving the third to the programmer. But we won’t have to really worry about some of these issues until later tutorials (when we discuss things like frame rates and animation). I just thought I’d get the idea planted in your head, so that if I bring it up later I don’t get blank stares (or obscenity-laden feedback).

Glut and OpenGL

As I said, the initialization code will differ from API to API. I chose to start with Glut, as the initialization code is pretty straightforward (at least, requires a little less brain power to fathom than DirectX). So here it is, our modified main function:

int main(int argc, char *argv){
  glutInitWindowPosition(100,100);
  glutInitWindowSize(200,200);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow("Foobar");
  glutDisplayFunc(draw);
  glClearColor(0,0,0,0);
  glClear(GL_COLOR_BUFFER_BIT);
  glutMainLoop();
 
return 0;
}

Code Sample 2 – modified main() function

That’s it – your first window. Let’s go through it line-by-line:

glutInitWindowPosition(100,100);

This function call tells Glut that the window that it creates will be at (100,100). This will put it in the upper-left corner of the monitor.

I can here the outcry now: “WAIT! What’s this? Upper left corner? Are you smoking crack? Basic geometry says that that will put a window in the LOWER left corner! How’d you get this job?” The reason for this requires a little bit of delving into how Windows (or any graphics driver, for that matter) handles drawing to the monitor. Yes, your standard Cartesian coordinate system will start from the lower left, with Y going “up” and X going “right”, but monitors are a bit different. On a monitor, the point (0,0) is actually at the upper-left corner of the screen. These are known as screen coordinates, or coordinates as seen by the monitor. What’s interesting about this is that, with a little googling on “Screen Coordinates”, you find out that this method of screen graphing is largely outdated (which makes one wonder why the creators of Glut went with this manner, considering that the transition to normalcy here is roughly two lines of code). Either way, this will really only come into play two times when programming games – during this initialization stage, and when handling mouse input (in a future tutorial), but it obviously deserves special mention here. So now you know, and knowing is half the battle.

glutInitWindowSize(200,200);

This function creates a window that is 200 pixels wide (first parameter) by 200 wide (second parameter). The pixels are counted to the right and downward from the window position, again following the screen coordinate system. If you create a window that is larger than the available window space, it does the standard windows it’s-still-there-just-not-on-the-desktop thing as if you had moved it too far towards one side of the monitor.

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

This function sets the display mode. The parameters here specify that the color mode will be RGBA (red, green, blue, alpha), and that the screen will be double-buffered. There is a list of options available for this function in the Glut documentation, but for now all you really need to know about this call is that double buffering is better than single as it helps to reduce screen flicker when a lot of stuff starts taking place on the screen.

glutCreateWindow("Foobar");

This function is the magical thing that makes this library slightly easier to use (in my opinion) than DirectX. This one statement takes all the data you’ve sent to Glut so far, and creates a window with the title passed into this function. No MFC, no fun with HWND objects, no message loops, just one function call.

glutDisplayFunc(draw);

This function tells Glut where the main drawing will take place. Glut will call this function every time that a glutPostRedisplay() function is called (we’ll cover this stuff later), and it is the responsibility of this function to make sure all the necessary drawing stuff gets to the screen. This function needs to either have been defined prior to the position of your main function in the same code file, linked in through a header, or it has to have a prototype listed (which is just good programming practice anyway). I tend to list all of my functions as prototypes, that way it makes it easy to reassign drawing functions for things like game over screens, intro screens, different game modes, etc. (yes, successive calls to this function take effect immediately, regardless of where this takes place).

glClearColor(0,0,0,0);

This function sets the overall background color of the window. This also sets the color that the screen will clear to when glClear() is called. This function accepts values on a unit scale – that is, any values you pass to this function must reside between 0 and 1. For those of you used to HTML color codes, you’ll have to do some dividing by 255 to yield these values. Just think of them as percentages (at least, thinking of them as percentages makes it easier for me) – (1.0,0,0,0) is 100% of the maximum red, 0% of the maximum blue, 0% of the maximum green, and 0% alpha blending (something we’ll cover in a later tutorial). If you do some searching on the Internet, you can get a list of colors and their corresponding RGB values.

glClear(GL_COLOR_BUFFER_BIT);

This function clears the buffer, leaving you with a nice clean screen to start with. While the function call is optional, you can get some interesting images if you forget it (sometimes it pops up with whatever you drew last, sometimes it does nothing). You can use this function whenever you need to clear the screen.

glutMainLoop();

This function tells Glut that you’ve finished your initialization, and are ready to start. At this point, control of the program flow passes from your main function to Glut, which loops in the background, simulating a message loop that you never have to mess with. Most of the code in your main function after this line may not be executed; I’m not very clear on the behavior of this function as it shuts down (I’ll figure this out by a later date, and post the results in a different tutorial).

Now on to the fun stuff – drawing. For now, most of our drawing will be rather simple, and will take place in our draw functions (later on in the series will cover things like having objects draw themselves, through the wonders of polymorphism). Here’s the draw function I’ve created for this tutorial ,which uses lines and dots to draw a stick figure to the screen:

void draw(){
  float x[360],y[360];
  for(int i = 0; i < 360; i++){
   x[i] = sin(((float)i*PI)/180)*25;
   y[i] = cos(((float)i*PI)/180)*25;
  }
  glOrtho(0,200,0,200,-1,1);

  glPointSize(2.0);
  glLineWidth(2.0);
  glColor3f(1,0,0);
  glBegin(GL_LINES);
   glVertex3d(75,50,0);
   glVertex3d(100,100,0);
   glVertex3d(125,50,0);
   glVertex3d(100,100,0);
   glVertex3d(100,100,0);
   glVertex3d(100,150,0);
   glVertex3d(75,125,0);
   glVertex3d(125,125,0);
   glVertex3d(125,115,0);
   glVertex3d(125,135,0);
   glVertex3d(125,135,0);
   glVertex3d(145,135,0);
 
   for(int j = 0; j<360; j++){
     glVertex3d(100 + x[j%360], 175 + y[j%360], 0);
     glVertex3d(100 + x[(j+1)%360], 175 + y[(j+1)%360],0);
   }
  glEnd();


  glBegin(GL_POINTS);
   glVertex3d(92,182,0);
   glVertex3d(108,182,0);
   glVertex3d(100,175,0);
  glEnd();

  glBegin(GL_LINES);
   glVertex3d(92,162,0);
   glVertex3d(108,162,0);
  glEnd();

  glutSwapBuffers();
  glutPostRedisplay();

}

Code Sample 3: Our first draw function

Don’t worry about how big this function looks – we have to draw vertex by vertex, so there’s bound to be a lot of data (and thus a lot of code). Let’s go through the more salient points of the function:

float x[360],y[360];
for(int i = 0; i < 360; i++){
  x[i] = sin(((float)i*PI)/180)*25;
  y[i] = cos(((float)i*PI)/180)*25;
}
 

This series of statements generates the points for the circular head. Sin() and cos() take radians, so a little conversion needs to be done, and they are supplied by the Math library that comes with all C++ installations. The main reason for doing this is that with all of the nifty stuff Glut does, it doesn’t provide a shortcut to draw a circle, so we must do things the hard way.

glOrtho(0,200,0,200,-1,1);

This function sets the bounds of the window (e.g. it establishes a grid of pixels that we can draw to). This is called a number of things, but I’m going to refer to it (probably interchangeably) as either model space or world coordinates. This function creates a grid that starts at 0 on the left, goes to 200 on the right, starts at 0 on the bottom, goes to 200 on the top, and covers z values from –1(far) to 1(near). Notice that the function operates differently than the screen coordinate system, and is more intuitive. The values can be set to whatever you want, but I like to define a model space to be roughly the bounds of the window, for my own peace-of-mind. Also note that these values can be positive and negative – they can define any space your mind can devise (the only factor is what will be manageable on the screen).

glPointSize(2.0);
glLineWidth(2.0);

These functions adjust the size of their respective elements. The glPointSize() command converts all points from one pixel to 2x2 pixels (or a square of 4 pixels), while the glLineWidth() command sets the width of the lines in the drawing to 2 pixels.

glColor3f(1,0,0);

This function sets the color of what you are drawing, using the same method as glClearColor() (minus the alpha channel). This particular call sets the program to draw in red.

glBegin(GL_LINES);
glEnd();

These calls tell Glut to start and stop drawing, respectively. The parameter passed into glBegin() tells the API what to do with the data between these calls. There is a list of different parameters, including:

GL_LINES – draw a series of lines to the screen (requires pairs of vertices to define each line)

GL_POLYLINE – same as draw lines, but instead of having to define the start and end point of each line, the most recent vertex serves as the beginning of the next line (kinda like connect the dots)

GL_POINTS – this just draws the points to the screen, one at a time.

GL_POLYGON – this draws a CONVEX polygon to the screen, using all of the supplied points as the boundary. If any of the points lie within the defined polygon (e.g. closer to the center than the adjacent two points), that point is ignored

GL_TRIANGLES – this takes sets of 3 vertices to draw successive triangles to the screen

GL_TRIANGLE_STRIP – to be honest, I don’t have much experience with this mode. I assume it’s similar to the polyline call, just how it handles it is not something I’m familiar with (yet)

Each of these parameters requires a different number of vertices, as specified above. The best way to find out what each does is to try it yourself – experimentation is the best way to learn.

glVertex3d(75,50,0);

This function defines a vertex at the point (75,50,0) (in world coordinates). How this vertex is actually used is left to the API. Note that these calls are only really useful between a glBegin() and glEnd() call, otherwise they’re discarded.

glutSwapBuffers();

As all of our drawing thus far has been to the back buffer, and not the screen, this command is required to take the information on the back buffer, and send it to the front in one massive swap. This is where what’s been drawn previously actually gets sent to the screen.

glutPostRedisplay();

This function alerts Glut that the screen needs to be redrawn, forcing it to call the draw() function again at the next possible opportunity. By including this immediately after the call to swap the buffers, we improvise our own display loop that runs as fast as the computer can handle. In general, you can put this command in any function that might modify what appears on the screen.

And that should cover it. With all of the above functions, you can draw very basic stuff to the screen. Tune in next month for the joys of getting input from the user, and playing around with it.

DirectX and the Art of Window Creation

DirectX takes a more mechanical approach to setting up the display. Please note that this section will most likely be less informational than the prior, as I’m still teaching myself the API. A lot of this stuff will look VERY similar to the tutorials included with the DirectX SDK. Please note that when you go to download the SDK, download the most recent version (the latest summer update), as that contains the newest code, and is what this tutorial will be based off of.

So let’s hop to it. Our fist task is to define our main function. For Windows programming, the general prototype of the main function needs to be modified. Where before we had:

int main(int argc, char *argv)

we now have:

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )

They do largely the same thing, but MFC is picky about what it gets.

Then we register our window class, so that Windows knows how to properly deal with the window.

Next, we set up our window. This is done with the following function calls:

WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                 GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                 "D3D Tutorial", NULL };

RegisterClassEx( &wc );

This set of calls creates a window class called “D3D Tutorial” and registers it with Windows. It also defines the message handler as MsgProc, which we’ll get to later. Then, the following call is executed to create the window:

HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01:                     
                                                   CreateDevice", WS_OVERLAPPEDWINDOW, 100,
                                                     100, 300, 300, GetDesktopWindow(), NULL,
                                                     wc.hInstance, NULL );
 

This call uses the previously registered class to create a window entitled “D3D Tutorial 01: CreateDevice”. The window is set to Overlapped mode, to allow for use of the minimize, maximize, and close buttons. The window is created at the screen coordinate position (100,100), and is 300 pixels tall by 300 wide. The program then relies on standard Win32 programming functions to update the window.

Next, we initialize Direct3D. For posterity’s sake, I’ve included the separate function distributed with the SDK for this part of the program:

HRESULT InitD3D( HWND hWnd )
{
  if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
       return E_FAIL;

  D3DPRESENT_PARAMETERS d3dpp;
  ZeroMemory( &d3dpp, sizeof(d3dpp) );
  d3dpp.Windowed = TRUE;
  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

  if( FAILED( g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,        
                                                     D3DDEVTYPE_HAL,
                                                     hWnd,
                                                     D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                                   &d3dpp, &g_pd3dDevice ) ) )
  {
   return E_FAIL;
  }

  return S_OK;
}

This function first creates the D3D device, and stores it to global variable g_pD3D (don’t worry, I’ll include the source file that defines all of this, so that things become clear). The only parameter ever passed to this function should be D3D_SDK_VERSION. Next, the variable d3dpp is created to store the various parameters that can be modified in a DirectX application. The call to ZeroMemory() zeroes out the parameter variable, and then the successive calls set them back up. This series of calls sets the windowed mode to “true” (meaning the application runs in its own window, as opposed to full-screen), sets the swap effect to discard (after the back buffer is swapped up front, it’s discarded), and doesn’t assign any kind of format to the back buffer (marking it as unknown).

Finally, the create device call is made. It calls upon DirectX to use the default video adapter, to do its rendering in hardware, its vertex processing in software (this can be changed to hardware to utilize some of the more nifty effects on newer video cards), then it passes in the device parameters and the device storage global. If at any point there is an error, the function tells the main function that something broke, then exits.

The next block of code serves two functions. First, upon the success of the initialization functions, the window is displayed. Then, the window is told to update. Finally, the program taps into the Windows message loop, to wait for a WM_PAINT message.

if( SUCCEEDED( InitD3D( hWnd ) ) )
{
  // Show the window
  ShowWindow( hWnd, SW_SHOWDEFAULT );
  UpdateWindow( hWnd );

  // Enter the message loop
  MSG msg;
  while( GetMessage( &msg, NULL, 0, 0 ) )
  {
   TranslateMessage( &msg );
   DispatchMessage( &msg );
  }
}

The ShowWindow() command shows the window, while the UpdateWindow() command updates the window for the first time (go figure). Following this, we define a message object to receive windows messages, then continue to loop while the program receives messages (this would be the game loop portion). TranslateMessage() translates the message to a useful format, and DispatchMessage() calls MsgProc (the function that we registered to the class earlier) to handle the message. Below is the sample message handler for this program:

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, 
                                                   LPARAM lParam )
{
   switch( msg )
   {
       case WM_DESTROY:
           Cleanup();
           PostQuitMessage( 0 );
           return 0;
       case WM_PAINT:
           Render();
           ValidateRect( hWnd, NULL );
           return 0;
   }

   return DefWindowProc( hWnd, msg, wParam, lParam );
}

This function only handles two messages: WM_PAINT, which tells the window to draw itself, and WM_DESTROY, which tells the program to end itself. The function Render() is where most of our drawing will take place. Following the drawing, we have to validate the window by using ValidateRect(), to ensure that everything is well. The Cleanup() function takes care of getting rid of our pointers, when the program is asked to exit.

And that should cover the set up of our DirectX application. Let’s move on to drawing!

Drawing in DirectX

As I stated earlier, most of our drawing will take place in the Render() function, which at the moment looks like this:

VOID Render()
{
  if( NULL == g_pd3dDevice )
   return;
  
  g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
                         D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
  
  if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
  {
   g_pd3dDevice->EndScene();
  
  }

  g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

Currently, all this function does is clear the screen to blue, then swap it to the screen. As with OpenGL, most of our drawing will take place between the BeginScene() and EndScene() commands. DirectX takes a bit of a different approach to drawing, however. DirectX relies on something called a vertex buffer. Don’t worry, it’s not all that complex, it’s simply a stored array of points of a specified size (we’re only going to draw a triangle today, so we’ll only have a vertex buffer of size three). Thus, instead of a list of vertex calls, we simply have a call that transforms a variable to serve the desired purpose.

First, we define some custom data types:

LPDIRECT3DVERTEXBUFFER9 g_pVB        = NULL;

struct CUSTOMVERTEX
{
   FLOAT x, y, z, rhw;
   DWORD color;
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

g_pVB is our vertex buffer holder, where the vertex data will actually be stored.  The struct CUSTOMVERTEX stores our vertex data, and D3DFVF_CUSTOMVERTEX sets the parameters that tell DirectX what to do with our custom vertex format (this particular call tells DirectX that our vertex has a transformed point followed by color data).

Next, we want to create our array of vertices and store them, using the following function:

HRESULT InitVB()
{
 CUSTOMVERTEX vertices[] =
 {
  { 150.0f,  50.0f, 0.5f, 1.0f, 0xffff0000, },
  { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },
  {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },
 };

 if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
  0, D3DFVF_CUSTOMVERTEX,
  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
 {
  return E_FAIL;
 }

 VOID* pVertices;
 if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
  return E_FAIL;
 memcpy( pVertices, vertices, sizeof(vertices) );
 g_pVB->Unlock();

 return S_OK;
}

The definition of vertices[] creates an array of 3 CUSTOMVERTEX objects, stored in the format:

{X, Y,Z, RHW, Color in HEX}, where color is 0xff followed by(RRGGBB)

As far as I can tell, the first “ff” in the color doesn’t do much of anything (yet). Next, we allocate the memory for the buffer, and specify what kind of data the buffer will contain, with the following command:

if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),
  0, D3DFVF_CUSTOMVERTEX,
  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
{
  return E_FAIL;
}

The call to CreateVertexBuffer allocates the appropriate amount of memory for the buffer, based on the intended size. Next, the buffer is first locked for editing, filled, and then unlocked, using the following sequence of commands:

VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ) ) )
  return E_FAIL;
memcpy( pVertices, vertices, sizeof(vertices) );
g_pVB->Unlock();

return S_OK;

The locking has to take place because the vertex buffer may reside in device memory (and thus could be in use by another procedure). The lock call tells the program the offset of the start of the lock (0), how much to lock (sizeof(vertices)), the third address is a pointer to vertex data, and the fourth parameter specifies the method of locking. Then, the data is copied from vertices to pVertices, storing the vertex data into the buffer using a basic memcpy command. Finally, the vertex buffer is unlocked, allowing free access to any who may desire it.


Now we get to render our scene. Below is the modified Render() function, with the necessary changes made:


VOID Render()
{
  if( NULL == g_pd3dDevice )
   return;

  g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
                                       D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
  
  if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
  {
     g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
     g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
     g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );
     g_pd3dDevice->EndScene();
  }

  g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

}

As before, the render function first clears the screen to blue. Where before there was nothing, the program now performs 3 vital functions. The call to SetStreamSource takes the vertex buffer source (g_pVB), and the size of each vertex in bytes (sizeof(CUSTOMVERTEX)). The extra 0s don’t do anything yet, but we need them for now. Next, the function identifies the flexible vertex format (FVF), which is our previously defined global type. Finally, DirectX is told to render the list of vertices as triangles. The first parameter is how to render the vertices (as a list of triangles), the second is the vertex index to start at (0), and the third parameter is how many triangles to render (1). After this, the drawing mode is ended, and the buffer is flipped to the screen.

And there you have it. I’ll leave it to you to do research on the types of primitives you can draw with DirectX, all it should take is modification of the Vertex buffer, proper reallocation of memory, and adjustment of the proper drawing calls.

Barring some slight modification of the main function (to accommodate the InitVB function), which is visible in the source file, that should about do it for this month. Fiddle around with the source files as presented. They are available as a single zip file at http://home.comcast.net/~evilstickman/techtrax/Article2.zip. Main.cpp contains the OpenGL code, and CreateDevice.cpp contains the DirectX code. Note that a separate project will have to be created for each code file. All projects should be just a standard C++ empty project. You’ll also want to make sure that the libraries have been set up properly, whether that be by following the Glut Readme file, or having the right version of the DirectX SDK installed (Summer Update from October 2003). If you run into any problems, do some searching on the Internet, or drop me a line at the address available on my Author page (I’ll try to help as much as I can).

 

Copyright (c) 2004 by Matthew Raymond Billock. All images and content on this website are original creations by Matthew Raymond Billock. Use of logos, images, or the Evil Stick Man Enterprises name without express written permission is prohibited