Sample ApplicationLet 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. Figure 3: Sample Application Step 1: Define Global Variables 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:
Step 2: 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
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.
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.
After setting the cooperative level, we set the display mode of the application using the SetDisplayMode method, as shown in figure 9.
Step 4: Create Front and Back Buffers 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 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 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
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 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 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 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. 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. Step 11: Render Loop 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.
|
|||||||||||