Microsoft KB Archive/172947

{|
 * width="100%"|

INFO: D3ddptri.exe Renders Triangle in Direct3D Immediate Mode

 * }

Q172947

-

The information in this article applies to:


 * Microsoft DirectX Software Development Kit, version 5.0
 * Microsoft Windows 2000 Advanced Server
 * Microsoft Windows 2000 Server
 * Microsoft Windows 2000 Professional

-

SUMMARY
DPTri is a sample that demonstrates Direct3D Immediate Mode to render a triangle using DrawPrimitive API. To close the application, select Esc and then Alt+F4.

The following file is available for download from the Microsoft Download Center:

"D3ddptri.exe" For additional information about how to download Microsoft Support files, click the article number below to view the article in the Microsoft Knowledge Base:

"Q119591 How to Obtain Microsoft Support Files from Online Services" Microsoft used the most current virus detection software available on the date of posting to scan this file for viruses. Once posted, the file is housed on secure servers that prevent any unauthorized changes to the file.

MORE INFORMATION
When you run the self-extracting executable, the following files are expanded:


 * D3d.ico (2Kb)
 * Dptri.c (42Kb)
 * Dptri.def (1Kb)
 * Dptri.exe (42Kb)
 * Dptri.mak (8Kb)
 * Dptri.rc (2Kb)
 * Dxerrors.c (13Kb)
 * Dxerrors.h (1Kb)
 * Readme.txt--you are reading this file
 * Resource.h (1Kb)

How the Sample Works
The following discussion is a "walkthrough" of the code.

The entry point of the sample is the same as any other Windows program, WinMain. This function is where all initialization takes place for the sample. The first initialization registers the window class and creates the window. After the window is successfully created, the program enters an infinite message loop until it closes. DirectX programs (particularly games) should only run when the program has the activation. If it loses activation, or if the program is paused, the program should just process Window's messages instead of running the game loop and trying to render.

There are no differences in registering the window class for a DirectX program. The sample registers the class with an icon. This icon is used by Windows to visibly represent the application on the task bar when the program loses activation with a task switch. No menu is used since the sample is a full-screen exclusive mode application. After the standard CreateWindow, ShowWindow, and UpdateWindow calls, the sample initializes DirectDraw and Direct3D in InitDDrawAndD3D.

The window callback procedure processes Windows messages. WM_ACTIVATEAPP must be handled for a "well-behaved" program. When the program loses activation, set a flag and do not enter the game loop. Let other programs use the CPU for a while. When the program gains activation, make sure to restore the program's internal status including DirectX. Hide the Windows cursor in the WM_SETCURSOR case as described. All DirectDraw programs should render their own cursor. Do not let Windows handle cursor rendering unless a flickering cursor is a feature of the program. Determine the cursor position with GetCursorPos then render your own software cursor. All the other messages are self-explanatory.

Initializing DirectDraw and Direct3D can be complex. The sample uses a user-defined data structure to hold some global information. The keywords for initialization are "never assume anything about the hardware." Follow these steps to initialize DirectDraw and Direct3D:


 * 1) Enumerate all DDraw supported video devices on the system. A system can have more than one video card. Use DirectDrawEnumerate to determine which is the best video card. In the enumerating callback procedure, check which device supports 3D, and then save that information. Make sure to enumerate the display mode that the application requires with EnumDisplayModes. Notice that this sample doesn't have that code. Do not assume that the particular mode that the program requires is supported on all devices. If a device does not support the required mode, have an alternative plan. Fall back on a different mode, if possible.
 * 2) The sample tries to use RGB color model. If 3D hardware is used, RGB color model is a must. In the case of software HEL, RGB is chosen if the processor supports MMX instructions. Note that starting with a DX5, MMX system will have two flavors of the RGB driver: the standard one, and an MMX enhanced one. The fallback is MONO color model when no acceleration is available.
 * 3) After a IDirect3D interface is queried off of a IDirectDraw interface, enumerate the devices supported. This is where you need to check for HW support for particular features if HW is to be used. If the program requires HW Z-buffer at 16bpp, fog, perspective corrected texturing, point lights, etc., then check and make sure. DrawPrimitive and its cousin APIs are methods of the IDirect3DDevice2 interface. First, get the IDirect3D2 interface by querying for it off of the IDirect3D interface. Then, use IDirect3D2::CreateDevice to get to IDirect3DDevice2. If the IDirect3DDevice interface is needed, use IDirect3DDevice2::QueryInterface. One point that this sample does not go into is the idea of enumerating all available devices and letting the user choose. Programs should profile the available devices and then display the options available to the user and have a default pre-selected.
 * 4) After all of DirectDraw and Direct3D devices have been enumerated, you need to create some surface for the rendering engine. You will need a primary surface with one back buffer and a Z buffer. Whether the device is HW or SW determines where the Z buffer should be allocated from. If it's HW then the Z buffer is allocated from video memory (if the HW supports Z buffers). If it's SW, create the surface in system memory.
 * 5) The last initialization this code performs is querying for the D3DDEVICE off the back buffer.

The key to this sample is the RenderTriangle function. An array of vertices is defined for one triangle with vertices at (0, 1, 0), (1, 0, 0), (-1, 0, 0). The normal of each vertex is -1 z, which points away from the monitor.

Setting up the matrices requires defining the world, view, and projection matrices, associate them with the IDirect3DDevice by calling IDirect3DDevice::CreateMatrix and IDirect3DDevice::SetMatrix methods.

In the SetupLightAndViewport function, a directional light is created and a viewport is setup to map the 3D world to the visible 2D window.

To give the triangle and background some visual appeal, it needs to have colors to define each. D3D materials are used for this purpose. If you use 3D HW acceleration, the color of the triangle will be green. If you use SW, the color will be red. On DX5, if MMX is detected, the triangle will be yellow. The background will always be a shade of blue.

The world, view, and projection transforms are specified with IDirect3DDevice2::SetTransform.

Gouraud shade is turned on with IDirect3DDevice2::SetRenderState along with other D3DRENDERSTATEs.

After you have created everything, you are ready to render the triangle. First, clear the viewport and Z-buffer with Clear. Then render the triangle with BeginScene, DrawPrimitive, and EndScene. Those 4 APIs usually are called together in that sequence. Make sure to not call SetRenderState with each frame. This is an expensive operation. If the render state doesn't change, then apply it once.

When using DrawPrimitive or DrawIndexedPrimitive, make sure to send as many triangles in one call as possible (that is, don't call the API for each triangle). Applications can minimize vertices to optimize for speed by rearranging a list of triangles to use D3DPT_TRIANGLESTRIP or D3DPT_TRIANGLEFAN.

With each frame, the triangle is animated by modifying the world matrix to apply a rotation around the Y-axis and then making the four rendering calls:

  IDirect3DViewport::Clear IDirect3DDevice2::BeginScene IDirect3DDevice2::DrawPrimitive IDirect3DDevice2::EndScene Finally, the image is made visible with a call to IDirectDrawSurface::Flip.

During execution the application may be deactivated and switched away. Make sure to pause all processing as mentioned earlier. When the application is activated, restore all surfaces and reload any artwork onto respective surfaces. The sample doesn't use any textures so IDDrawSurface::Restore is sufficient.

When cleaning up, release all objects in the reverse order of creation. Notice that only the front buffer is released, and that the back and the Z buffer are not released. Releasing a surface with attached surfaces will release all attached surfaces.