Step by Step: Developing Windows Mobile Game-Quality Graphics with DirectX

4/7/2010

Microsoft Corporation

February 2007

Summary

Learn about the new Windows Mobile 5.0 features of Microsoft DirectX in this self-paced hands-on lab (HOL). You will explore the various drawing capabilities provided by DirectX such as drawing primitives, lighting, and depth. You will also learn how to identify the DirectX capabilities of a particular device. For developers who are familiar with Windows Mobile Game API (GAPI), each section contrasts the DirectX capabilities with the corresponding features of GAPI. This HOL will take 1 hour and 15 minutes to complete (Optional labs may take an additional hour). (41 printed pages)

Download code sample from the Microsoft Download Center.

Applies To

Microsoft Direct3D Mobile

Microsoft DirectDraw

Microsoft Visual Studio 2005

Windows Mobile version 5.0 software for Pocket PCs

Windows Mobile version 5.0 software for Smartphones

The following applications are required to run this HOL:

  • Microsoft Windows XP Professional
  • Visual Studio 2005
    This HOL requires Visual Studio 2005 Standard, Professional, or Team System Editions. It will not work with any of the Express Editions. If you do not have the correct edition of Visual Studio 2005, find out how you can acquire it from the Visual Studio 2005 Developer Center.
  • Microsoft ActiveSync 4.0 or later
    ActiveSync 4.0 and ActiveSync 4.1 allows for connectivity between a Windows Mobile-based device and your computer. Download and install ActiveSync 4.1.
  • Windows Mobile 5.0 SDKs.
    The Windows Mobile 5.0 SDKs for Pocket PC and Smartphone enable development for Windows Mobile-based devices in Visual Studio 2005.
    Download and install Windows Mobile 5.0 SDK for Pocket PC.
    Download and install Windows Mobile 5.0 SDK for Smartphone.
    For the best experience with this HOL, you should have a Windows Mobile 5.0 Pocket PC. Although the HOL will run on a Windows Mobile 5.0 emulator, Direct3D Mobile and DirectX are designed to take advantage of device hardware to provide high-speed drawing features; as a result, programs that take advantage of Direct3D Mobile and DirectX tend to experience slow performance when they are run on an emulator.
  • Set up your computer
    Follow the directions in the Additional Information section to set up your computer for this HOL.

Introduction

Lab 1: Creating the Direct3D Mobile Device

Lab 2: Understanding Perspective View and Lighting in Three Dimensions (Optional)

Lab 3: Understanding DirectDraw Functionality- The 'CAPS' Bit (Optional)

Conclusion

Additional Information

Introduction

With the release of Windows Mobile 5.0, there are a number of new technologies designed to aid application development and create a wide variety of content for the entire spectrum of devices. In particular, there are two new technologies that have been added to the Windows Mobile 5.0 application suite: Microsoft Direct3D Mobile and Microsoft DirectDraw. Each of these graphics technologies have been designed to provide the application developer with a powerful, fully featured alternative to the current high-performance GAPI.

Although GAPI addresses a number of areas of development (graphics and input), the introduction of DirectX technologies to Windows Mobile is only designed to provide alternatives to the graphics implementations. By providing Direct3D Mobile and DirectDraw to the Windows Mobile suite of development technologies, the application developer is provided a powerful set of tools that spans high-level scene generation to low-level hardware access.

This HOL introduces the developer to the world of DirectX graphics development with emphasis on the APIs and technology, in addition to the theory behind some of the more advanced concepts—in particular, three-dimensional development.

This HOL is designed to cover the native aspect of development with the DirectX graphics APIs.

Developers with GAPI experience should note that while GAPI is a powerful API, as you transition your development away from a basic frame buffer design to that of a complex two- and three-dimensional API, there are a number of theories and practical implementation considerations to be aware of. To help supplement your experience, refer to the full DirectX documentation for the desktop and general graphics literature.

Lab 1: Creating the Direct3D Mobile Device

Direct3D Mobile is a powerful new graphics API provided in Windows Mobile 5.0 to replace other graphics development technologies (in particular the Windows Mobile Game API or GAPI). Direct3D Mobile provides much more than the basic frame buffer access as GAPI did; rather, Direct3D Mobile provides an entire graphics API to develop both two- and three-dimensional applications.

Direct3D Mobile and corresponding driver support will be included on all Windows Mobile devices, creating a powerful platform on which to target a wide variety of devices. These devices include a wide variety of resolutions and architectures—some even providing two- and three-dimensional graphics hardware that can be used by the application without the need to write to the low-level hardware directly. In this lab, you will explore how to create a Direct3D Mobile device that is extensible to target a number of different platforms.

While GAPI incorporates a rather rudimentary method of initializing and targeting the frame buffer of the target device, Direct3D Mobile requires the inclusion of a specific driver attached to the Direct3D Mobile device. The driver and corresponding runtime will be responsible for all graphics processing and eventual processing to the display system.

In this HOL, you will perform the following exercises:

  • Creating a Direct3D Mobile application
  • Creating triangles and shading
  • Modifying the triangle's properties

Exercise 1: Creating a Direct3D Mobile Application

In this exercise, you will build a Direct3D Mobile application that is capable of running on a physical device.

To open the example solution

  1. On the desktop computer, click Start | All Programs | Microsoft Visual Studio 2005 | Microsoft Visual Studio 2005.

  2. In Visual Studio 2005, click File | Open | Project.

  3. In the Open Project dialog box, browse to C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_HOL402\Exercises\CreateDevice.

  4. Select CreateDevice.sln, and then click Open. The CreateDevice solution opens.

  5. In Solution Explorer, expand the Source Files node, and then double-click createdevice.cpp to open the createdevice.cpp file. This file has been started for you, and you will complete the implementation for creating a Direct3D device.

    Note

    The solution in its current state will not build properly because the necessary Direct3D Mobile type and function declarations are not yet available. After you complete Step 1 in the following procedure, the solution will build properly.

To set up the application

  1. At the top of the createdevice.cpp file, add a #include statement for the Direct3D Mobile interface header file, d3dm.h, immediately following the other #include statements, as shown in the following code example.

    #include <d3dm.h>
    
  2. Notice the global variable declarations that follow the #include statements. The g_pD3DM global variable is a reference to the IDirect3DMobile interface of the main Direct3D Mobile object. This interface is used to enumerate the devices available on the system and to query devices for their capabilities and other information. The IDirect3DMobile interface is also used to retrieve an IDirect3DMobileDevice implementation. This interface represents a given Direct3D Mobile device. The application stores the IDirect3DMobileDevice interface reference in the g_pd3dmDevice global variable. The remaining global declarations will be used and explained in Exercise 2 of this lab.

    Note

    Only one Direct3D Mobile object may exist on the system at one time; consequently, only one application may use Direct3D Mobile at any one time. This limitation may be removed in future releases.

    Dynamic rotation is not currently supported on Windows Mobile 5.0–based devices because a bug exists that will be fixed in a future update to Windows Mobile 5.0. To help aid in detecting and addressing screen rotation in this application, the IsScreenRotated and ShowRotatedScreenMessage functions have been provided.

  3. Review the implementation of the IsScreenRotated and ShowRotatedScreenMessage functions. IsScreenRotated uses the ChangeDisplaySettings function to determine the current display rotation. A display rotation of DMDO_DEFAULT indicates that the screen is not rotated. The ShowRotatedScreenMessage function simply displays an error message by using a message box.

    Note

    The name of the ChangeDisplaySettings function may be confusing because it implies that the display is being modified. This method can also be used to interrogate for the current state of certain values as is being done in the IsScreenRotated function.

  4. Locate the InitD3DM function, which has been started for you. The function receives an HWND named hWnd and begins by declaring and initializing a D3DMPRESENT_PARAMETERS structure named d3dmpp. The d3dmpp structure will be used when you create the Direct3D Mobile device.

  5. Immediately following the d3dmpp declaration, create the main Direct3D Mobile object by calling the Direct3DMobileCreate function, passing it the D3DM_SDK_VERSION identifier. Assign the returned interface pointer to g_pD3DM, as shown in the following code example.

    g_pD3DM = Direct3DMobileCreate(D3DM_SDK_VERSION);
    

    Note

    The D3DM_SDK_VERSION identifier is passed to the Direct3DMobileCreate function to ensure that an application was built against the correct header files for the device's current Direct3D Mobile environment. This number is incremented whenever a header (or other) change would require applications to be rebuilt. If the version doesn't match, Direct3DMobileCreate will fail.

    If the Direct3D Mobile object creation fails for any reason, the application should not continue.

  6. Add an if statement that compares the value of g_pD3DM to NULL, and return E_FAIL if it is, as shown in the following code example.

    if (g_pD3DM == NULL)
        return E_FAIL;
    
  7. Set up the d3dmpp D3DMPRESENT_PARAMETERS structure that will be used to create the Direct3D Mobile device by adding the following code. Set the Windowed member to TRUE because the application will be displaying its content in a window.

    d3dmpp.Windowed = TRUE;
    d3dmpp.SwapEffect = D3DMSWAPEFFECT_DISCARD;
    d3dmpp.BackBufferFormat = D3DMFMT_UNKNOWN;
    

    Note

    Setting Windowed member to FALSE would indicate that the application is using a full–screen display.

    Direct3D Mobile presents graphics data by means of a process called scene presentation, using what is called a viewport or frame. The viewport uses two buffers: a back buffer and a front buffer. Graphics data is placed in the back buffer, which is then moved to the front buffer where it is displayed. There are several ways of swapping the data from the back buffer to the front buffer; each of which falls under one of two main swapping categories: flipping, which switches the identity of the two buffers, and blitting, which copies data from one buffer to another. The SwapEffect member of the D3DMPRESENT_PARAMETERS structure controls this behavior.

  8. Set it to D3DMSWAPEFFECT_DISCARD, which is the most efficient way of presenting the back buffer on the display.

  9. Request a back buffer format that matches the current desktop display format by setting the BackBufferFormat member to D3DMFMT_UNKNOWN.

  10. Declare an HRESULT variable named hResult, assigning it the return value of the g_pD3DM CreateDevice method, as shown in the following code example.

    HRESULT hResult = g_pD3DM->CreateDevice(
        D3DMADAPTER_DEFAULT, D3DMDEVTYPE_DEFAULT,
        hWnd, 0, &d3dmpp, &g_pd3dmDevice);
    

    For the purposes of this lab, you should use the default hardware device, so pass the D3DMADAPTER_DEFAULT identifier as the first parameter of CreateDevice.

    The second parameter should always be D3DMDEVTYPE_DEFAULT—the only value currently supported by that parameter.

    Note

    Direct3D Mobile currently only supports the hardware abstraction layer (HAL) device; therefore, the device type is always specified as D3DMDEVTYPE_DEFAULT. However, when specifying the adapter type, you have the choice to use the default or one that was previously registered by a call to the RegisterSoftwareDevice method. To use the registered adapter, specify D3DMADAPTER_REGISTERED_DEVICE as the adapter type, otherwise specify D3DMADAPTER_DEFAULT.

    As the third parameter, pass the window handle, hWnd, which was passed to the InitD3DM function. The window referenced by this handle will be the focus for all Direct3D Mobile operations, and it will remain the focus until the device object is destroyed.

    The fourth parameter should be 0.

    Note

    It is possible to pass the D3DMCREATE_MULTITHREADED identifier as this parameter to support request multithread-safe behavior, but that is beyond the scope of this lab.

    As the fifth parameter, pass a reference to the d3dmpp structure that you set up to describe the device's presentation parameters.

    The resulting Direct3D Mobile device that is created by a successful call to the CreateDevice method is assigned to the final parameter, so pass a reference to the g_pd3dmDevice global variable.

  11. Test the result of the CreateDevice method call. Add an if statement, and then use the FAILED macro to test the hResult value, returning E_FAIL if the value indicates that the device creation failed, as shown in the following code example.

    if (FAILED(hResult))
        return E_FAIL;
    

    The last line of the function is already typed for you. It returns S_OK, which indicates success.

  12. Verify that the completed InitD3DM function looks like the following code example.

    HRESULT InitD3DM(HWND hWnd)
    {
        D3DMPRESENT_PARAMETERS d3dmpp = {0}; 
    
        g_pD3DM = Direct3DMobileCreate(D3DM_SDK_VERSION);
        if (g_pD3DM == NULL)
            return E_FAIL;
    
        d3dmpp.Windowed = TRUE;
        d3dmpp.SwapEffect = D3DMSWAPEFFECT_DISCARD;
        d3dmpp.BackBufferFormat = D3DMFMT_UNKNOWN;
    
        HRESULT hResult = g_pD3DM->CreateDevice(
            D3DMADAPTER_DEFAULT, D3DMDEVTYPE_DEFAULT,
            hWnd, 0, &d3dmpp, &g_pd3dmDevice);
        if (FAILED(hResult))
            return E_FAIL;
    
        return S_OK;
    }
    
  13. Locate the Cleanup function, which has already been created for you. Notice that it frees and releases resources that are created and used while the application is running.

  14. Locate the Render function, which has been started for you. You will complete the implementation to create a blue background. The function begins by checking to see if g_pd3dmDevice is equal to NULL, ensuring that the global Direct3D Mobile device has been successfully created. Immediately following the condition and return statement, call the g_pd3dmDevice Clear method to create a blue background. This method clears the Direct3D Mobile viewport—or a set of rectangles in it—to a specific red, green, blue (RGB) color, clears the depth buffer, and erases the stencil buffer, as shown in the following code example.

    g_pd3dmDevice->Clear(0, NULL, D3DMCLEAR_TARGET,
        D3DMCOLOR_XRGB(0,0,255), 1.0f, 0);
    

    Pass 0 as the first parameter and NULL as the second parameter, indicating that the entire viewport should be affected, rather than an array of rectangles.

    Pass the D3DMCLEAR_TARGET flag as the third parameter, indicating that the target of the clear operation is to be set to the color passed as the fourth parameter. For the fourth parameter, use the D3DMCOLOR_XRGB macro, passing (0,0,255) to specify that the color should be blue.

    Note

    The RGB values could be changed to any combination (for example, red (255,0,0)) to set the background color. When applying this background color, Direct3D automatically selects whether to perform the action using hardware acceleration or, when no hardware acceleration is available, using software. This is a distinct improvement over GAPI, which puts the burden on the application to detect and determine whether to use hardware or software to perform the action.

    The final two parameters are used to control the depth and the stencil buffer. Both are beyond the scope of this exercise, so pass 1.0f and 0 respectively.

  15. Declare an HRESULT variable named hResult, and then assign it the return value of the g_pd3dmDevice BeginScene method, as shown in the following code example. As you might expect, this method begins a Direct3D Mobile scene.

    HRESULT hResult = g_pd3dmDevice->BeginScene();
    
  16. Test the result of the BeginScene method call by adding an if statement and using the SUCCEEDED macro to test the hResult value. If the call was successful, call the g_pd3dmDevice EndScene method, as shown in the following code example.

    if (SUCCEEDED(hResult))
        g_pd3dmDevice->EndScene();
    
  17. After the if statement and the EndScene method call, call the g_pd3dmDevice Present method, passing it all NULL parameters, as shown in the following code example.

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

    The Present method causes the scene that has been created on the back buffer (with BeginScene and EndScene) to be displayed to the user on the front buffer by using the swapping method specified by the D3DMPRESENT_PARAMETERS structure's SwapEffect member as previously described.

    Note

    If your migrating an application from the GAPI development model, BeginScene and EndScene are analogous to the GXBeginDraw() and GXEndDraw() functions.

  18. Verify that the completed Render function looks like the following code example.

    VOID Render()
    {
        if (g_pd3dmDevice == NULL)
            return;
    
        g_pd3dmDevice->Clear(0, NULL, D3DMCLEAR_TARGET,
            D3DMCOLOR_XRGB(0,0,255), 1.0f, 0);
    
        HRESULT hResult = g_pd3dmDevice->BeginScene();
        if (SUCCEEDED(hResult))
            g_pd3dmDevice->EndScene();
    
        g_pd3dmDevice->Present(NULL, NULL, NULL, NULL);
    }
    
  19. Locate the MsgProc function, which serves as the main message procedure for the application. Notice that when the WM_CLOSE message is received, the Cleanup function is called, and that when the WM_PAINT message is received, the Render function that you just completed is called. Notice that the WM_KEYDOWN part of the case statement closes the application when the ESC key is pressed; the WM_LBUTTONUP part of the case statement closes the application when the screen is tapped with the stylus.

    Because Direct3D Mobile does not currently support rendering while the screen is rotated, the application needs to close when the screen is rotated. The operating system sends the WM_SETTINGCHANGE message whenever the screen orientation changes, so you need to add code to handle this situation.

  20. Add a new case to the switch (msg) statement for the WM_SETTINGCHANGE message, as shown in the following code example.

    case WM_SETTINGCHANGE:
        break;
    
  21. Before the case's break statement, add an if statement, calling the IsScreenRotated function that you previously completed. In the event that the screen is rotated, use the PostMessage function to close the application, as shown in the following code example.

    if (IsScreenRotated())
        PostMessage(hWnd, WM_CLOSE, 0, 0);
    
  22. Verify that the completed MsgProc function looks like the following code example.

    LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
    {
        switch (msg)
        {
        case WM_LBUTTONUP: 
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
    
        case WM_KEYDOWN:
            if (VK_ESCAPE == wParam)
                PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
    
        case WM_CLOSE:
            Cleanup();
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    
        case WM_PAINT:
            {
                PAINTSTRUCT ps;
                HDC hDC = BeginPaint(hWnd, &ps);
                Render();
                EndPaint(hWnd, &ps);
                return 0;
            }
    
        case WM_SETTINGCHANGE:
            if (IsScreenRotated())
                PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
        }
    
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    
  23. Locate the WinMain function, which has already been started for you. It begins by declaring an HRESULT variable named hResult.

  24. To ensure that the application is not displayed if the screen is rotated when the application starts, add an if statement immediately following the hResult declaration. The if statement should call the IsScreenRotated function and return 0 if IsScreenRotated returns true, as shown in the following code example.

    if (IsScreenRotated())
        return 0;
    

    Before the application's window is displayed, you need to initialize Direct3D Mobile.

  25. Locate the call to the ShowWindow function about two-thirds of the way into the WinMain function.

  26. Immediately before the call to the ShowWindow function, call the InitD3DM function that you previously completed.

  27. Pass the hWnd value returned by the CreateWindow function a few lines earlier and assign the value returned by InitD3DM to the hResult variable, as shown in the following code example.

    hResult = InitD3DM(hWnd);
    
  28. Immediately after the call to the InitD3DM function (still before ShowWindow is called), add an if statement and use the FAILED macro to test the value of hResult. If the InitD3DM function call fails, return the E_FAIL value, as shown in the following code example.

    if (FAILED(hResult))
        return E_FAIL;
    
  29. Verify that the completed WinMain function looks like the following code example.

    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPTSTR szCmd, INT )
    {
        HRESULT hResult;
    
        if (IsScreenRotated())
            return 0;
    
        WNDCLASS wc = { 0L, MsgProc, 0L, 0L, 
            hInst, NULL, NULL, NULL, NULL,
            TEXT("D3DM Tutorial") };
        RegisterClass(&wc);
    
        int iScreenWidth = GetSystemMetrics(SM_CXSCREEN);
        int iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    
        HWND hWnd = CreateWindow(TEXT("D3DM Tutorial"), 
            TEXT("D3DM Tutorial 01: CreateDevice"), 
            WS_VISIBLE, 
            0, 0, iScreenWidth, iScreenHeight,
            NULL, NULL, wc.hInstance, NULL);
    
        SHFullScreen(hWnd, SHFS_HIDESIPBUTTON | SHFS_HIDETASKBAR);
    
        hResult = InitD3DM(hWnd);
        if (FAILED(hResult))
            return E_FAIL;
    
        ShowWindow(hWnd, SW_SHOWNORMAL);
        UpdateWindow(hWnd);
    
        MSG msg = {0}; 
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return 0;
    }
    

To build the project and run the application

  1. In Visual Studio 2005, click Build | Build Solution. This step should build the project successfully, but the build will display two warnings regarding the variables hResult and pVertices being unreferenced. These warnings are OK at this point; they'll be corrected in the next exercise. If any errors occur or if you receive any warnings other than the two mentioned, verify your code against the code in Step 29 of the "To set up the application" procedure.

  2. In the Device drop-down list, verify that Windows Mobile 5.0 Pocket PC Device is selected, as shown in Figure 1.

    Bb278112.370216ae-ba9e-4e31-b268-d37cab4f064a(en-us,MSDN.10).gif

    Figure 1. Device drop-down list

  3. In Visual Studio 2005, click Debug | Start Debugging to run the application.

  4. Verify that the application creates a Direct3D Mobile device that occupies the entire screen and fills it with blue (RGB 0,0,255), as shown in Figure 2.

    Bb278112.b21c58b4-fed7-4c2f-ae2c-233e54fe212b(en-us,MSDN.10).gif

    Figure 2. A Direct3D Mobile device is created with a blue screen

  5. Click Debug | Stop Debugging to stop running the application.

Exercise 2: Creating Triangles and Shading

In this exercise, you will modify the first example that you created to include a shaded triangle on the device. The triangle is the fundamental unit of three-dimensional graphic designs. While GAPI provides low-level access to the frame buffer, the creation of triangles in Direct3D Mobile uses inherent constructs of the API. Triangles and other primitive types are native additions to the Direct3D Mobile technology and provide high-level graphics tools that will be new to the GAPI developer. These primitive types can be used for both two-dimensional and three dimensional scene generations.

To create a triangle

  1. Return to the top of the createdevice.cpp file, and then notice the remaining declarations. There is an LPDIRECT3DMOBILEVERTEXBUFFER global variable named g_pVB. The global variable g_pVB serves as an interface reference to a buffer to hold the drawing vertices. There is a structure named CUSTOMVERTEX that is used to describe the coordinates and color of the drawing vertices. Finally, there is the D3DMFVF_CUSTOMVERTEX macro. This macro specifies the flexible vertex format (FVF) parameters that you will use when creating the vertex buffer later in this exercise.

  2. Locate the InitVB function that has been started for you. This function creates a Vertex Buffer (VB) to hold the vertices that this example uses for its triangle creation.

    The InitVB function begins by declaring four variables, and then declares and initializes a CUSTOMVERTEX array named vertices.

    The InitVB function calls the g_pd3dmDevice GetDeviceCaps method to retrieve the device's capabilities, storing the results in the D3DMCAPS structure named caps, and returns E_FAIL if the GetDeviceCaps call fails.

    The function performs a bitwise AND operation on the SurfaceCaps property of the caps structure and the D3DMSURFCAPS_VIDVERTEXBUFFER identifier. If this condition evaluates to true, indicating that the D3DMSURFCAPS_VIDVERTEXBUFFER bit is set, the device supports creating vertex buffers in video memory; otherwise, the vertex buffer will have to be created in system memory. Video memory provides better performance than system memory, so the application uses video memory if the device supports it. The D3DMPOOL variable named pool indicates which type of memory to use when creating the vertex buffer. If the device supports video memory, the function assigns the enumeration value D3DMPOOL_VIDEOMEM to the pool variable; otherwise, the function assigns the enumeration value D3DMPOOL_SYSTEMMEM.

  3. Immediately following the line that assigns the D3DMPOOL_SYSTEMMEM value to the pool variable (outside the prior if...else condition), call the g_pd3dmDevice >CreateVertexBuffer method, storing the result in the hResult variable that was predeclared, as shown in the following code example.

    hResult = g_pd3dmDevice->CreateVertexBuffer(
        3*sizeof(CUSTOMVERTEX), 0,
        D3DMFVF_CUSTOMVERTEX, pool, &g_pVB);
    

    Pass three times the size of the CUSTOMVERTEX structure (because there are three vertices) as the first parameter, indicating to the device to allocate enough vertex buffer memory to hold a CUSTOMVERTEX array with three elements.

    As the second parameter, pass 0. This usage flag setting is not used in this lab.

    Pass the predeclared D3DMFVF_CUSTOMVERTEX macro as the third parameter, describing the vertex format.

    As the fourth parameter, pass the pool variable to tell the function which memory pool to use for storing the vertex: video memory or system memory.

    An interface reference to the resulting vertex buffer is stored in the fifth parameter, so pass a reference to the predeclared g_pVB global variable. This parameter is set only when the CreateVertexBuffer function returns successfully.

  4. Add an if statement that uses the FAILED macro to test the hResult value, and return the E_FAIL value if the CreateVertexBuffer method call fails, as shown in the following code example.

    if (FAILED(hResult))
        return E_FAIL;
    

    Now that the vertex buffer has been created, you need to fill it. To fill the vertex buffer, you need to lock the vertex buffer to gain access to the vertices because the vertex buffer may be in memory that is accessible by other processes.

  5. Add a call to the g_pVB Lock method, storing the result in the hResult variable, as shown in the following code example.

    hResult = g_pVB->Lock(0, sizeof(vertices), &pVertices, 0);
    

    Pass 0 as the first parameter, which is an offset into the vertex to be locked.

    As the second parameter, pass the size of the vertices array to indicate how many bytes are to be locked.

    Pass a reference to the predeclared pVertices pointer as the third parameter. Upon the successful return of the Lock method, pVertices will contain the address of the locked vertex buffer memory.

    Pass 0 as the final parameter; this flags parameter is not used in this lab.

    Note

    Similar to many graphics implementations (such as GAPI), there is a discrete concept of locking and unlocking the buffer, essentially raising and lowering a flag to other processes, that indicates that the buffer is being written to. If this approach were not used, race conditions could result during memory modification, and it could ultimately lead to memory corruption.

  6. Add an if statement that uses the FAILED macro on the hResult value, and return the E_FAIL value if the Lock method call failed, as shown in the following code example.

    if (FAILED(hResult))
        return E_FAIL;
    
  7. Store the list of vertices in the vertex buffer by using memcpy to copy the contents of the vertices array to the address identified by the pVertices pointer, and then unlock the vertex buffer memory by calling the g_pVB >Unlock method, as shown in the following code example.

    memcpy(pVertices, vertices, sizeof(vertices));
    g_pVB->Unlock();
    

    The last line of the function has been typed for you. It returns the S_OK identifier value.

  8. Verify that the completed InitVB function looks like the following code example.

    HRESULT InitVB()
    {
        D3DMCAPS caps;
        D3DMPOOL pool;
        void* pVertices;
        HRESULT hResult;
    
        CUSTOMVERTEX vertices[] =
        {
            { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
            { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
            {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
        };
    
        if (FAILED(g_pd3dmDevice->GetDeviceCaps(&caps)))
            return E_FAIL;
    
        if (caps.SurfaceCaps & D3DMSURFCAPS_VIDVERTEXBUFFER)
            pool = D3DMPOOL_VIDEOMEM;
        else
            pool = D3DMPOOL_SYSTEMMEM;
    
        hResult = g_pd3dmDevice->CreateVertexBuffer(
            3*sizeof(CUSTOMVERTEX), 0,
            D3DMFVF_CUSTOMVERTEX, pool, &g_pVB);
        if (FAILED(hResult))
            return E_FAIL;
    
        hResult = g_pVB->Lock(0, sizeof(vertices), &pVertices, 0);
        if (FAILED(hResult))
            return E_FAIL;
        memcpy(pVertices, vertices, sizeof(vertices));
        g_pVB->Unlock();
    
        return S_OK;
    }
    

    You need to modify the Render function, so it will not only draw the background as it did in the first example, but it will also draw the triangle that you are creating.

  9. In the Render function, locate the line after the call to the BeginScene method that reads if (SUCCEEDED(hResult)).

  10. Immediately following the link in Step 9, create a new line and type an opening { brace because you will be adding more statements inside the conditional if block to be run when the BeginScene call succeeds.

    You will be passing the vertices down a stream, so first you need to specify the source of that stream, which is the vertex buffer.

  11. Create another new line after the { brace you just typed. The new line should be before the EndScene method call. On the new line, call the g_pd3dmDevice SetStreamSource method, as shown in the following code example.

    g_pd3dmDevice->SetStreamSource(0, g_pVB, sizeof(CUSTOMVERTEX));
    

    The first parameter identifies the number of the data stream to use. The only value that Direct3D Mobile supports is 0.

    As the second parameter, pass the g_pVB vertex buffer whose data will be used to render the triangle.

    As the final parameter, pass the size of the CUSTOMVERTEX structure.

  12. Call the g_pd3dmDevice >DrawPrimitive method that does the actual rendering of the geometric primitive, as shown in the following code example.

    g_pd3dmDevice->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 1);
    

    The first parameter indicates which geometric primitive to draw. Passing the enumeration value D3DMPT_TRIANGLELIST as the first parameter indicates that you want to draw one or more triangles. The vertices used to draw the geometry come from stream number 0, which is bound to the video buffer by the earlier call to the SetStreamSource method. Each primitive uses a predefined number of vertices. In the case of a triangle, each triangle uses three vertices: a line uses two vertices and a point uses one.

    The second parameter indicates the offset into the vertex buffer of the first vertex to use. In this lab, pass 0 to indicate that drawing should start with the first vertex.

    The last parameter indicates the number of primitive geometries to render. It is the developer's responsibility to insure that enough vertices are available in the vertex buffer to satisfy the number of geometries being rendered. For example, drawing one triangle requires that at least three vertices be in the vertex buffer; drawing two triangles requires that at least six vertices be in the vertex buffer, and so on. In this lab, pass a 1 for this parameter indicating that the DrawPrimitive method should draw one triangle.

  13. Immediately following the line that calls the EndScene method, terminate the if block by creating a new line and typing a closing } brace. (The remaining call to the Present method should still be outside the if block.)

  14. Verify that the updated Render function looks like the following code example.

    VOID Render()
    {
        if (g_pd3dmDevice == NULL)
            return;
    
        g_pd3dmDevice->Clear(0, NULL, D3DMCLEAR_TARGET,
            D3DMCOLOR_XRGB(0,0,255), 1.0f, 0);
    j
        HRESULT hResult = g_pd3dmDevice->BeginScene();
        if (SUCCEEDED(hResult))
        {
    g_pd3dmDevice->SetStreamSource(0, g_pVB, sizeof(CUSTOMVERTEX));
            g_pd3dmDevice->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 1);
            g_pd3dmDevice->EndScene();
        }
    
        g_pd3dmDevice->Present(NULL, NULL, NULL, NULL);
    }
    

    You must also modify the WinMain function, so it creates the vertex buffer and launches the new program logic.

  15. Locate the line in the WinMain function that calls the ShowWindow function. You will be enclosing that statement and all of the statements through the while loop in a condition that depends on the success of a call to the InitVB function, so immediately before the line that calls ShowWindow, create a new line.

  16. On the new line, call the InitVB function, and then assign its return value to the hResult variable, as shown in the following code example.

    hResult = InitVB();
    
  17. Add an if statement (still before the ShowWindow function call), using the FAILED macro, to test the hResult value returned by the InitVB function. If the InitVB function call fails, return the E_FAIL value, as shown in the following code example.

    if (FAILED(hResult))
        return E_FAIL;
    
  18. Locate and modify the while condition a few lines down to negatively compare the message property of the msg instance to the WM_QUIT message. In other words, you want the while block to loop as long as the message does not indicate that the application should terminate, as shown in the following code example.

    while (msg.message != WM_QUIT)
    
  19. Inside the while loop and before the TranslateMessage function call, add an if statement that calls the PeekMessage function. Be sure to include the opening { brace because you'll be enclosing the TranslateMessage and DispatchMessage lines in the if block, as shown in the following code example.

    if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
    {
    

    First, pass a reference to the msg variable. The msg variable receives the currently queued message information upon successful return.

    The second parameter should be NULL, indicating that messages for any window in the current thread should be retrieved.

    The minimum and maximum message filters should both be 0U.

    Pass the PM_REMOVE identifier to indicate that all messages should be removed from the queue after retrieval (except WM_PAINT, which is always processed before removal).

  20. After the existing DispatchMessage function call, create a new line and terminate the if block with a closing } brace.

  21. On the next line, create an else block corresponding to the if block you just added. As its single instruction, on the next line, call the Render function, as shown in the following code example.

    else
        Render();
    

    Note

    You may want to clean up the indentation of your code at this point, if necessary. An easy way to clean up indentation is to highlight the code, and then click Edit | Advanced | Format Selection in Visual Studio.

  22. Verify that the updated WinMain function looks like the following code example.

    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPTSTR szCmd, INT )
    {
        HRESULT hResult;
    
        if (IsScreenRotated())
            return 0;
    
        WNDCLASS wc = { 0L, MsgProc, 0L, 0L, 
            hInst, NULL, NULL, NULL, NULL,
            TEXT("D3DM Tutorial") };
        RegisterClass( &wc );
    
        int iScreenWidth = GetSystemMetrics(SM_CXSCREEN);
        int iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    
        HWND hWnd = CreateWindow(TEXT("D3DM Tutorial"), 
            TEXT("D3DM Tutorial 01: CreateDevice"), 
            WS_VISIBLE, 
            0, 0, iScreenWidth, iScreenHeight,
            NULL, NULL, wc.hInstance, NULL);
    
        SHFullScreen(hWnd, SHFS_HIDESIPBUTTON | SHFS_HIDETASKBAR);
    
        hResult = InitD3DM(hWnd);
        if (FAILED(hResult))
            return E_FAIL;
    
        hResult = InitVB();
        if (FAILED(hResult))
            return E_FAIL;
        ShowWindow(hWnd, SW_SHOWNORMAL);
        UpdateWindow(hWnd);
    
        MSG msg = {0};
        while (msg.message != WM_QUIT)
        {
            if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
                Render();
        }
    
        return 0;
    }
    

To build the project and run the application

  1. In Visual Studio, click Debug | Start Debugging to rebuild the project and run the application.

    The application should display the original blue background and a shaded green triangle, as shown in Figure 3.

    Bb278112.874e9057-3264-414d-a62d-513280829291(en-us,MSDN.10).gif

    Figure 3. Adding a triangle

  2. Click Debug | Stop Debugging to stop the running application.

Exercise 3: Modifying the Triangle's Properties

In this exercise, you will modify the fundamental aspects of a triangle in three-dimensional space to change not only its physical characteristics as presented on screen, but also the color associated with them.

To modify color

  1. Find the vertices array declaration in the InitVB() function. It should look like the following code example.

    CUSTOMVERTEX vertices[] =
    {
        { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
        { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
        {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
    };
    

    Each element of the array has five values; however, only four of those values are of interest. The first three values are the X, Y and Z coordinates that represent the position of each vertex in three-dimensional space. The last parameter represents color. The fourth parameter, RHW, is outside the scope of this lab. Each element in the array represents a single vertex with color value that, when used together, create the triangle you see on screen.

    The color parameter determines the color at each vertex point and will be used in the particular blending for this example.

  2. Modify the first vertex color parameter to be 0xffffffff, as shown in the following code example.

    { 150.0f,  50.0f, 0.5f, 1.0f, 0xffffffff },
    
  3. Rebuild the application, and then run it.

  4. Note the result of the first vertex and the impact on the entire triangle, as shown in Figure 4.

    Bb278112.135074ac-0dc3-456c-a071-a4fdb0d894a9(en-us,MSDN.10).gif

    Figure 4. Modifying the color of one of the triangle's vertices

  5. Stop the running application.

  6. Modify the colors, so they are all the identical for each vertex (perhaps 0x00ff00ff), as shown in the following code example.

    { 150.0f,  50.0f, 0.5f, 1.0f, 0x00ff00ff },
    { 250.0f, 250.0f, 0.5f, 1.0f, 0x00ff00ff },
    {  50.0f, 250.0f, 0.5f, 1.0f, 0x00ff00ff }
    
  7. Rebuild, and then deploy the project.

  8. Note the color behavior of the triangle when all of the colors at each vertex point are the same. Unlike the earlier examples in which color blend is interpolated across the distance from each vertex point and its corresponding color, identical colors do not exhibit such behavior, as shown Figure 5.

    Bb278112.427fa51b-574f-4320-bc33-2039a4f882d9(en-us,MSDN.10).gif

    Figure 5. All vertices' colors are the same

  9. Stop the running application.

  10. Revert the vertices array back to the values from the original project, as shown in the following code example.

    { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
    { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
    {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
    

To modify shape

  1. Locate the Vertex Buffer creation in the InitVB() function (which should look like the original again), as shown in the following code example.

    CUSTOMVERTEX vertices[] =
    {
        { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
        { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
        {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
    };
    
  2. Note the coordinate values that are defined as the first three parameters of each vertex. These represent the X, Y and Z coordinates of the vertex. In particular, note the third parameter for each vertex (0.5f), which represents the Z value. In three-dimensional programming, the Z coordinate represents the depth, and because all values are equal in this example, the triangle to appears to be on a flat surface perpendicular to the user. Increasing the value of the Z coordinate for a vertex moves that vertex deeper into three-dimensional space.

    Modifying any of the coordinates within a vertex will cause the triangle's position to change.

  3. Set the third vertex's X and Y values to 200.0f, as shown in the following code example.

    { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
    { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
    { 200.0f, 200.0f, 0.5f, 1.0f, 0xff00ffff }
    
  4. Rebuild, and then redeploy the project.

  5. Note the changes to the triangle, as shown Figure 6.

    Bb278112.86a942bd-053f-4814-9580-6ec4a5e126f4(en-us,MSDN.10).gif

    Figure 6. Changing each of the third vertex's first two coordinates (X & Y) to 200.0f

  6. Stop the running application.

    Note

    If time permits, feel free to experiment by modifying the other coordinate values.

  7. Revert the vertices array back to the values from the original project, as shown in the following code example.

    { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
    { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
    {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
    

For this portion of the lab, you are going to examine the primitive types that are associated with Direct3D Mobile development. Unlike GAPI, which handles drawing by placing pixels into the frame buffer, Direct3D Mobile provides basic primitive drawing types. Direct3D Mobile is the only graphics API that includes a number of basic primitive types and representation methods. Having performed the previous procedures in this exercise, you are already familiar with the triangle primitive.

To modify type

  1. Locate the original vertex list, as shown in the following code example.

    CUSTOMVERTEX vertices[] =
    {
        { 150.0f,  50.0f, 0.5f, 1.0f, 0x00000000 },
        { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00 },
        {  50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff }
    };
    
  2. Find the following code in the Render() function.

      g_pd3dmDevice->SetStreamSource(0, g_pVB, sizeof(CUSTOMVERTEX));
      g_pd3dmDevice->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 1);
    

    Note

    The SetStreamSource function determines the vertices that are included for processing in the scene. In this procedure, you are passing in the vertex buffer as previously outlined. The DrawPrimitive function determines what you are drawing with those vertices. From the above example, you can see that you are drawing a triangle.

  3. Modify the code to use line types rather than triangles. Change the primitive type in the call above from D3DMPT_TRIANGLELIST to D3DMPT_LINESTRIP, as shown in the following code example.

    g_pd3dmDevice->DrawPrimitive(D3DMPT_LINESTRIP, 0, 1);
    
  4. Rebuild, and then deploy the project.

Conclusion

You should see a single line on the display fading from the color of the first vertex to that of the second vertex as previously described, as shown in Figure 7.

Bb278112.7c388b25-8df7-49bd-b4a7-36edf2097d93(en-us,MSDN.10).gif

Note

An important aspect of Direct3D Mobile development that differs from other graphics models is how scenes are generated and ultimately rendered to the display. Because three-dimension is based on camera view and perspective (this also applies to two-dimension-like development), it is possible to render primitives that are off screen. Determining how to keep pixels on screen and their use in creating more complex designs are important.

Summary

In this lab, you performed the following exercises:

  • Creating a Direct3D Mobile application
  • Creating triangles and shading
  • Modifying the triangle's properties

In this lab, you learned how to create a Direct3D Mobile device for Windows Mobile 5.0 that is capable of running on an actual device. Variations of the previous examples are included in the Windows Mobile Pocket PC SDK for future reference.

Lab 2: Understanding Perspective View and Lighting in Three Dimensions (Optional)

The objective of this lab is to understand the fundamentals of three-dimensional perspectives when displaying objects and to understand the basics of lighting in Direct3D Mobile.

In this lab, you will perform the following exercises:

  • Understanding and modifying perspective of a given object
  • Understanding and modifying lighting parameters in a Direct3D Mobile scene

Exercise 1: Understanding and Modifying Perspective of a Given Object

In this exercise, you will build a project that includes perspective matrix transformations, and you will understand the implications of this design.

Unlike in a GAPI-based application, the creation of three-dimensional objects and their presentations to viewers are native aspects of the API. Understanding how to modify these will allow you to create three dimensional scenes and effects based on the core of the API.

To open the perspective matrix example solution

  1. In Visual Studio, open the example solution at C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_HOL402\Exercises\Matrices\Matrices.sln.

  2. On the Device toolbar, verify that Windows Mobile 5.0 Pocket PC Device.

  3. Build and deploy the project to view the original state of the project (by clicking Debug | Start Debugging).

    In this application, there is some form of animation—in this case, by the rotation of a single triangle in three-dimensional space based on the perspective view from the camera, as shown in Figure 9.

    Bb278112.25c49650-e901-4aa7-a8ec-5decb601ea71(en-us,MSDN.10).gif

    Figure 9. A rotating triangle

  4. Stop the running application (by clicking Debug | Stop Debugging).

  5. In Solution Explorer, open the matrices.cpp source code file under the Source Files folder.

  6. In Code view, locate the SetupMatrices() function.

  7. Read the comments in this function and notice that it sets up several transformation matrices using the LPDIRECT3DMOBILEDEVICE SetTransform method.

To use the eye point matrix

  1. Locate the following matrix definition, as shown in the following code example.

    D3DMXVECTOR3 vEyePt( 0.0f, 3.0f, -5.0f );
    

    This code determines the viewing position to the triangle in the scene in three-dimensional coordinate space (X, Y, Z).

  2. Modify the Z value (the third component) to a greater negative value than what is presented (for example, -25.0f).

  3. Rebuild, and then deploy the project.

  4. Notice the difference in the view of the triangle and the behavior, as shown in Figure 10. By moving the viewing position farther back, the triangle appears further in the distance.

    Bb278112.989c6712-61c1-430a-b52f-8e7e3dbe245a(en-us,MSDN.10).gif

    Figure 10. The triangle appears further in the distance, and rotation is performed better

  5. Stop the running application (by clicking Debug | Stop Debugging).

  6. Modify the value in the opposite direction—perhaps to -2.0f.

  7. Rebuild, and then deploy the project.

  8. Notice the change in view of the triangle, as shown in Figure 11.

    Bb278112.b5b63d7f-7f19-4768-b5f0-6d766be30dd9(en-us,MSDN.10).gif

    Figure 11. The triangle appears closer and rotates more slowly

  9. Stop the running application (by clicking Debug | Stop Debugging).

  10. Return the values to the original coordinates as previously defined.

To use the look-at point matrix

  1. Locate the look-at point matrix, as shown in the following code example.

        D3DMXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    

    The coordinate values (X, Y, Z) start with the view matrix and "looks" in the direction defined, in this case, the origin.

  2. Modify any one of the parameters a slight degree (for example, 2 or 3).

  3. Rebuild, and then deploy the project.

  4. Notice the difference in view from the original project design. Based on the offset of where the camera is looking and the fixed position of the triangle, the triangle appears as if it is in a different position to the end user, as shown in Figure 12.

    Bb278112.4b1acfc8-40e8-4b03-bed4-41350e2cf3ef(en-us,MSDN.10).gif

    Figure 12. The X coordinate of the look-at point has been changed to 2.0f

  5. Stop the running application (by clicking Debug | Stop Debugging).

  6. If desired, return the values to their original coordinates at the origin, and then modify several of the coordinates.

  7. Rebuild, and then deploy the project.

    Does the behavior match with what you expected?

  8. Stop the running application (by clicking Debug | Stop Debugging).

  9. Return the values to the original coordinates as previously defined.

  10. In Visual Studio, click File | Close Project.

You should now have an understanding of eye and look matrices for objects in three-dimensional space.

Exercise 2: Understanding and Modifying Lighting Parameters in a Direct3D Mobile Scene

In this exercise, you will modify the basic lighting elements in a three-dimensional scene. Unlike GAPI, Direct3D Mobile includes native support for lighting three-dimensional objects through a variety of methods. Rather than creating custom light tables or coloring for specific polygons as would be undertaken in GAPI, Direct3D Mobile can be used to light scenes as they are incorporated in the API.

To open the lighting example solution

  1. In Visual Studio, open the example solution at C:\Program Files\Windows Mobile Developer Samples\HOLs\MEDC06_HOL402\Exercises\Lights\Lights.sln.

  2. In the Device toolbar, verify that Windows Mobile 5.0 Pocket PC Device is selected.

  3. Build and deploy the project to view the original state of the project (by clicking Debug | Start Debugging). The project opens, as shown in Figure 13.

    Bb278112.958e8a90-adba-4e2d-a95a-45c97dd5def8(en-us,MSDN.10).gif

    Figure 13. Rotating, lighted cylinder

  4. Close the application (by clicking Debug | Stop Debugging).

To add lighting parameters

  1. In Solution Explorer, open the lights.cpp source code file.

  2. Locate the SetupLights() function.

  3. Find the lighting render state, which indicates whether lighting should be included in the scene or not. You can find this state in the following call.

    g_pd3dmDevice->SetRenderState( D3DMRS_LIGHTING, TRUE );
    
  4. Change the lighting render state value to FALSE, as shown in the following code example.

    g_pd3dmDevice->SetRenderState( D3DMRS_LIGHTING, FALSE );
    
  5. Rebuild the project, and then deploy it.

    Notice the change in appearance from the original scene, as shown in Figure 14.

    Bb278112.bcff009a-b237-4897-b5ec-6cfdd1521826(en-us,MSDN.10).gif

    Figure 14. Lighting is not included in the scene

    You should observe that while lighting can be enabled for a given scene, if it is not enabled, only the material characteristics as previously defined in this function will be exhibited. Lighting for a specific light is toggled from the LightEnable call, as shown in the following code example.

    g_pd3dmDevice->LightEnable( 0, TRUE );
    
  6. Close the application (by clicking Debug | Stop Debugging).

  7. Return the lighting render state to TRUE.

  8. Rebuild and deploy the project to ensure that the lighting is as it was during the initial state of the project.

  9. Modify the LightEnable parameter for the first light by changing this to FALSE, as shown in the following code example.

    g_pd3dmDevice->LightEnable( 0, FALSE );
    
  10. Rebuild and deploy the project and observe the behavior, as shown in Figure 15.

    Bb278112.0bac7781-850e-49ed-83d7-08fea8806533(en-us,MSDN.10).gif

    Figure 15. The first light is turned off individually

  11. Close the application (by clicking Debug | Stop Debugging).

  12. Return the LightEnable state to TRUE.

  13. Rebuild, and then deploy the project to ensure that the lighting is as it was during the initial state of the project.

  14. Close the application (by clicking Debug | Stop Debugging).

    In this particular scene, you are using a white directional light.

  15. Locate the following code in the example.

    light.Diffuse.r  = *(D3DMVALUE*)&f; // 1.0f;
    light.Diffuse.g  = *(D3DMVALUE*)&f; // 1.0f;
    light.Diffuse.b  = *(D3DMVALUE*)&f; // 1.0f;
    
  16. Modify the red and blue values to 0, as shown in the following code example.

    light.Diffuse.r  = 0.0f; // 1.0f;
    light.Diffuse.g  = *(D3DMVALUE*)&f; // 1.0f;
    light.Diffuse.b  = 0.0f; // 1.0f;
    
  17. Rebuild, and then deploy the application.

    With lighting now enabled, the only diffuse color component for the directional light will be green, which you should observe as the color of the cylinder, as shown in Figure 16.

    Bb278112.0d336afc-0682-4932-8878-d417247711f0(en-us,MSDN.10).gif

    Figure 16. The light color is green

  18. Close the application (by clicking Debug | Stop Debugging).

  19. Zero out all of the color components for this light.

  20. Rebuild, and then deploy the application.

    You will notice that with no color channel components enabled for the light that the resulting view for the cylinder lacks any color components—showing black in the final view, as shown in Figure 17.

    Bb278112.58695946-8912-4b97-99ed-769518106b6b(en-us,MSDN.10).gif

    Figure 17. No light colors

  21. Close the application (by clicking Debug | Stop Debugging).

  22. In Visual Studio, click File | Close Project.

Summary

In this lab, you performed the following exercises:

  • Understanding and modifying perspective of a give object
  • Understanding and modifying lighting parameters in a Direct3D Mobile scene

In this lab, you have learned the impact of projection matrices in creating a three-dimensional scene. Also, you have learned how to toggle light implementations and the impact of various parameters.

Lab 3: Understanding DirectDraw Functionality- The 'CAPS' Bit (Optional)

The objective of this lab is to review and understand the way in which platform functionality (both hardware and software based) is exposed with DirectX—in particular with DirectDraw. While DirectDraw is a powerful two-dimensional API, it is most closely related to the GAPI graphics implementation when dealing with surfaces presentation.

In this lab, you will learn the basis in which DirectX functionality is exposed and how to view these parameters both programmatically and through the Visual Studio 2005 debugger. The foundation of the functionality is the DirectDraw Capability Bit (also known as the CAPS Bit).

In this lab, you will perform the following exercise:

  • Learning about DirectDraw capability bits

Exercise 1: Learning About DirectDraw Capability Bits

In this exercise, you will learn the basis of capability bits in DirectDraw and how to view these parameters both in system code and through the Visual Studio debugger.

To open the example solution

  1. In Visual Studio, open the example solution at C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Samples\CPP\Win32\Directx\Ddraw\DDex1\DDex1.sln.

  2. In Solution Explorer, open the DDex1.cpp source code file.

    Note

    Do not build and deploy the project yet.

For those developers who are migrating from the GAPI environment, you should be familiar with the basic methods of determining system characteristics in regards to both graphics and input. While the scope of these methods were rather basic due to the design of the API and supporting platform architectures, the DirectDraw capability bits are far greater in scope and are designed to expose both software and hardware functionality that does not exist in GAPI.

In migrating from GAPI, the fundamental return characteristics in the GXDisplayProperties structure (from the GXDisplayProperties() functional call) returns a populated structure with rudimentary information regarding surface support types and frame buffer access.

For DirectDraw, a GetCaps() method returns both the software (DDHEL—Hardware Emulation Layer) as well as hardware (DDHAL—Hardware Abstraction Layer, if applicable). While the DirectDraw CAPS values that are returned include basic information similar to those of the GAPI calls, the DirectDraw CAPS mechanism returns dozens more that can be used to create applications capable of targeting a wide variety of platforms and also providing the best experience on a given device.

To view capability bits

  1. In Visual Studio, build and deploy DDex1 (by clicking Debug | Start Debugging).

    Note

    If you are using the emulator or a Toshiba E750/Himalaya, deploying the application will fail. An error message appears on the screen. This message results from not running a device that includes the necessary capability bits. This is expected behavior.

  2. From the source file, find the #include section that includes the <ddraw.h> header file.

  3. Right-click this file, and then open it in Visual Studio 2005.

To use capability bits

  1. In the DDex1.cpp source file, include a breakpoint at line 335, as shown in the following code example.

    if (!(ddCaps.ddsCaps.dwCaps & DDSCAPS_BACKBUFFER)) 
        {
            return InitFail(hWnd, E_FAIL, szNoBackBufferMsg);
        }
    
  2. Run the program with the debugger, which should bring you to the breakpoint.

  3. In the debugger's Auto pane, notice the variable listing.

  4. Expand the ddcaps tree, and then view all of the capability bits exposed in this structure.

    Note

    Notice the various capability bits that are exposed from this particular call—in particular, those that are listed as 0 (or not supported). Alpha and Color key CAPS are good examples.

    In the previous call, the failure in question on this platform can be traced back to the DDSCAPS_BACKBUFFER surface CAP.

  5. In the debugger's Auto pane, expand the ddscaps tree. You should find a value associated with this.

  6. In the ddraw.h header file, search for DDSCAPS_BACKBUFFER.

  7. Cycle on this search until you've found the #define section that maps this and other capability bit values for surface caps. You should find a series of ddscaps definitions that map to values.

  8. Find the one definition that matches the value for your current platform. This is the current surface CAPS defined for this platform.

    Note

    Notice the other values and the one associated with the BACKBUFFER caps in addition to other values.

Learning to map these return values is one method of determining the support on a given platform and of creating code that is extensible enough to run on a wide variety of devices while taking advantage of all system characteristics.

To use capability bits across platforms

  1. Use a different platform from the platform you used in the "To use capability bits" procedure. You can use any other platform (including an emulator), although a device with hardware acceleration would be preferable.

  2. Remove the breakpoints from the code, and then run DDex1 by following the procedure "To use capability bits".

    Note

    If you are running a platform that supports hardware flipping and multiple back buffers, the results should be different than the first time you ran this code. If, on the other hand, the capability bits for this new platform match those of the earlier platform in this area, the results should be similar.

  3. On this new platform, run the program with the debugger, which should bring you to the same breakpoint in the "To use capability bits" procedure.

  4. In the debugger's Auto pane, expand the general capability bits and the surface caps.

    Note

    Look at the values of the various capability bit fields. How do these differ from the platform you had used previously? Take particular note of the differences on a hardware-accelerated platform (such as the Dell Axim).

  5. Close the currently running application (by clicking Debug | Stop Debugging).

  6. Insert a breakpoint at line 333.

  7. Rerun the program with the debugger.

  8. In the debugger's Auto pane, note the additional variables and the capability bits they represent. If you would like, compare these values against various platform targets at your discretion.

Conclusion

In this lab, you performed the following exercise:

  • Learning about DirectDraw capability bits

Terminating an Application That Is Running on a Device or Emulator

This appendix describes how to terminate an application that is running on a device or emulator. This is useful for cases where an application has been started without having the debugger attached and the application needs to be terminated so a new copy of the application can be deployed. You will terminate the application by using the Remote Process Viewer remote tool in Visual Studio.

Before you can terminate a process, you need to know the name of the executable file. In most cases, this is the same name as the Visual Studio project. If you are uncertain about the name of the executable file, you can find it in the project's properties.

To terminate an application that is running on a device or emulator by using the Remote Process Viewer remote tool

  1. In Visual Studio, click Project | xxx Properties, where xxx represents the name of the current project.

  2. In the Project Properties dialog box, note the value in the Assembly Name field. This is the name that the executable file will be running on the device or emulator.

  3. Close the Properties dialog box.

    Now, you can terminate the process.

  4. On the desktop computer, click Start | Microsoft Visual Studio 2005 | Visual Studio Remote Tools | Remote Process Viewer.

  5. When prompted by the Select a Windows CE Device dialog box, select the emulator or device where the application is running, as shown in Figure 18, and then click OK.

    Bb278112.82634017-0e47-4d15-8770-dd8065f4806e(en-us,MSDN.10).gif

    Figure 18. Select a Windows CE Device dialog box

  6. After the connection to the emulator or device completes, select the application that you want to terminate in the top pane of the Remote Process Viewer, as shown in Figure 19.

    Bb278112.db46cecf-2b67-446b-b186-a24e80c1c8c9(en-us,MSDN.10).gif

    Figure 19. Selecting the application to terminate

    You may need to widen the Process column (the leftmost column) to fully view the process names.

  7. In Windows CE Remote Process Viewer, click File | Terminate Process to terminate the process.

    Note

    Be certain that the correct process is selected before you click Terminate Process. Terminating the incorrect process may render the device or emulator unusable, requiring it to be reset before you can use it again.

  8. Verify that the process is terminated by selecting Target | Refresh. Scroll to the top pane of Windows CE Remote Process Viewer again. If the application name still appears, the process was not terminated, and you need to repeat these steps.

Conclusion

Note

Most processes terminate in one try; however, depending on the state of the application, terminating a process occasionally takes two attempts.

Setting Up Your Computer

The following lab files are required to set up your computer to run this HOL:

  • Pre-existing Visual Studio 2005 project, code, and data files.

To install these files to the correct path, download and run the following installation program:

MEDC06_HOL402.msi.

Note

If you used an emulator in a previous HOL, you should do a hard reset on the emulator before starting this HOL. (On the emulator, click File | Reset | Hard.)

Note

If you receive an error during deployment that indicates that the process or file is in use, this means that the program is still running on the emulator and must be closed before a new copy can be deployed and run. This error may appear anytime in the lab that you deploy the emulator. See Terminating an Application That Is Running on a Device or Emulator in the Additional Information section for instructions about exiting a running application.