Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
99 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

  Contents

 Introduction
 What is IM?
 Execute Buffers
 Steps to Create
 an Application

 Scene Management
 Sample App
 IM Objects
 DrawPrimitive
 Comparing Modes
 Summary

 Printable version

 


Sample Application

Let us now consider a sample application and go through its source code, to understand the steps required to create an immdiate mode application.

In this application, we will be going through the standard procedure to initialize a Direct3D immediate mode application. The scene to be rendered consists of three vertices making up a triangle. These points are used to setup the execute buffer, which is then rendered onto the screen.

Source Listing 2 shows a sample screen image of the running application.

triangle
Figure 3: Sample Application

Step 1: Define Global Variables

Popup : Figure 4

In this step, we have put together all the required variables into one structure, for a matter of convenience, during initialization. Some of the variables used are:

  • dev - pointer to the Direc3DDevice object
  • view -pointer to the Direct3DViewport object
  • DriverGUID - this array stores pointers to the selected devices. From this list of devices, one device which best suits our needs will be selected
  • BPP - represents the number of bits-per-pixel and decides the colour depth. This information is used to decide the colour model used
  • CurrDriver - indicates the driver to be used from the list of drivers available
  • DDSFront, DDSBack - two DirectDraw surfaces, used for drawing and flipping contents
  • d3dEXData - buffer for storing execute buffer data
  • lpD3DExBuf - pointer to execute buffer
  • viewpos - view matrix

Step 2: Setup Windows

RegisterClass(&wc); hWnd = CreateWindow("Immediate", "D3D Immediate Mode", WS_OVERLAPPEDWINDOW, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL); globals.hWnd = hWnd; globals.hInstance = hInstance;
Figure 5: Step2 : Setup Windows

In this step, shown in figure 5, we create the window on which the scene is to be rendered.

Step 3: Setup Direct3D

hdc = GetDC(globals.hWnd); globals.BPP = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(hWnd, hdc);
Figure 6: Step 3: Setup Direct3D

In this step, we first query the Windows system to get the bits-per-pixel information. Based on this, we can decide the other display parameters for the application, like colour model.

Then we initialize DirectDraw, as shown in figure 7 using the DirectDrawCreate function.

DirectDrawCreate(NULL, &lpDD, NULL);
Figure 7: Initialize DirectDraw

After initializing DirectDraw, we set the cooperative level of the application, as shown in figure 8 using the SetCooperativeLevel method of the DirectDraw object. An application can operate at different cooperative levels. In the exclusive mode, the application is the only one receiving the events and has exclusive control of the machine. Normally, applications operate in full screen and exclusive mode. In full screen mode, the complete display is occupied by the current application. An application can also work in the non-exclusive mode and the windowed mode. During debugging, the windowed mode is recommended. The full screen, exclusive mode is similar to the one available to full screen DOS applications.

lpDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
Figure 8: Setting the Cooperative Level

After setting the cooperative level, we set the display mode of the application using the SetDisplayMode method, as shown in figure 9.

lpDD->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, BIT_DEPTH);
Figure 9: Set the Display Mode

Step 4: Create Front and Back Buffers

Popup : Figure 10

In this step, as shown in figure 10, we create two buffers, namely the front buffer and the back buffer, for rendering. As direct rendering to the screen is a slow, we first render to one of the buffers, which is then transferred to the screen. To create these buffers, we have to first obtain the primary surface using the CreateSurface method. After creating the primary surface, we can get the back buffer from it, using the GetAttachedSurface method.

Step 5: Create Z-Buffer

Popup : Figure 11

In this step, as shown in figure 11, we create a surface for the Z buffer. If Z buffer is supported by the hardware, this buffer will be used to compute the Z values. The Z buffer is created using the CreateSurface method. After creating the Z buffer, it is attached to the back buffer using the AddAttachedSurface method.

Step 6: Initialize Direct3D

Popup : Figure 12

In this step, as shown in figure 12, after creating and attaching the buffers, we setup the viewport parameters and inittialize Direct3D. In this step, we get the Direct3D interface using the QueryInterface method and enumerate the device using the EnumDevices function. The EnumDevices method is the same as the one discussed during the coverage of retained mode in [10].

Step 7: Create Viewport

lpD3D->CreateViewport(&globals.view, NULL); globals.dev->AddViewport(globals.view); globals.view->SetViewport(&viewData);
Figure 13: Step 7: Create Viewport

As shown in figure 13, after creating the Direct3D object, we create and initialize the viewport using the CreateViewport, AddViewport and SetViewport methods.

Step 8: Create Lights

Popup : Figure 14

As shown in figure 14, after creating the viewport, we create the lights using the CreateLight method and add the lights to the viewport using the AddLight method.

Step 9: Create Execute Buffer

Popup : Figure 15

As shown in figure 15, after creating the lights, we create the execute buffer to hold the data to be rendered. The size of the buffer is calculated using the sizes of the data to be stored, which in this example, is three vertices, two instructions and one triangle information. An execute buffer is created using the CreateExecuteBuffer method. After creating the buffer, we lock it using the Lock method, till we finish adding data to it, at which point, we release the lock.

Step 10: Add Data

Popup : Figure 16

As shown in figure 16, after creating the execute buffer, we add data into the buffer in the proper order. To the buffer, we first add the three vertices that make up the triangle, then we add the D3DOP_TRIANGLE instruction, followed by the triangle information. In the triangle information, we set the flag to draw all three edges. After the triangle information, we add the D3DOP_EXIT instruction, to indicate end of the execute buffer, as shown in figure 17.

Popup : Figure 17

After filling the data into the execute buffer, we unlock it using the Unlock method. After unlocking the buffer, we set the other parameters of the execute buffer and make it ready for execution.

To render the buffer, we call BeginScene on the device, after which, we execute the buffer using the Execute method. After executing the buffer, we call EndScene on the device, as shown in figure 18.

Popup : Figure 18

Step 11: Render Loop

Popup : Figure 19

As shown in figure 19, after adding the data to the execute buffer and executing it once, we display the window using the ShowWindow function and go into an infinite loop, waiting for events. If there are any events, they are processed by the event handler. If there are no events, the application can do idle processing. Usually, in this state, the application renders the next frame. In our application, we keep rendering the same frame, by executing the same execute buffer over and over.

The full source code of the application is presented in appendix A.



Next : IM Objects