Graphics with DirectX in Windows Mobile 5.0 Games

 

Frank Lee Wilson

with Jim Wilson
JW Hedgehog, Inc.

March 2006

Applies to:
   Windows Mobile version 5.0
   Microsoft Direct3D Mobile
   Microsoft DirectDraw

Summary: Learn how to develop graphics with DirectX in games for Windows Mobile 5.0 powered devices, and learn the differences between the Windows Mobile 5.0 implementation of Direct3D and DirectDraw versus their desktop computer counterparts. (24 printed pages)

Download Graphics_with_DirectX_WM5_Games.msi from the Microsoft Download Center.

Contents

Introduction
History of Windows Mobile Game Development
Direct3D Mobile Compared to Direct3D for Desktop Computers That Run Windows
Native Code Development
Managed Code Development
Conclusion

Introduction

Gaming on mobile devices has become very popular in recent years. The hardware capabilities of these devices are better than ever. Many new devices that contain improvements like very high quality displays, faster processors, more memory, and hardware three-dimensional acceleration pave the way for developing games that take advantage of all these features. As a developer, having to manage and write graphics code to suit each device can be difficult. Thanks to Direct3D Mobile and DirectDraw, application programming interfaces (APIs) within the DirectX family, developing games for Windows Mobile 5.0 powered devices is much more manageable than in the past.

Direct3D Mobile and DirectDraw are device-independent APIs to common graphics-related needs in game development. Developers have used DirectX on desktop computers for years, in addition to variants of DirectX on Microsoft Xbox and Microsoft Xbox 360, so developers coming from the desktop computer and game console worlds will be able to easily switch to Windows Mobile 5.0 game development with very little ramp-up time. Because DirectX has such a long history, there is a wealth of documentation and information available about DirectX development that should help ease the transition for mobile game developers who are not already familiar with DirectX.

Direct3D Mobile and DirectDraw for Windows Mobile 5.0 powered devices expose both managed and native APIs, allowing developers to choose which implementation is right for them and their games.

This article primarily targets developers who are switching from desktop computer game development to Windows Mobile 5.0 game development. This article discusses how game development on Windows Mobile powered devices has changed, compares the primary differences between Direct3D Mobile and DirectDraw versus their desktop computer counterparts, and provides some examples of Direct3D Mobile development that use managed and native code.

History of Windows Mobile Game Development

Prior to the addition of Direct3D and DirectDraw to Windows Mobile 5.0, developers used the Game API (GAPI) or Graphical Device Interface (GDI). GAPI has been deprecated and the use of Direct3D and DirectDraw are now encouraged. This change is appropriate because Direct3D and DirectDraw provide all of the graphical abilities that GAPI provides in addition to many more. Developers still use GDI as necessary because it provides support for many two-dimensional drawing functions. The following section discusses the abilities of each of these APIs.

A Comparison of GAPI, GDI, DirectDraw, and Direct3D Mobile

The following APIs have different strengths and weaknesses for graphics in game development. The following sections discuss the features that are natively included in each of the APIs.

GAPI

GAPI provides the most basic graphics features. It gives you direct, exclusive access to the video frame buffer in most cases. In some cases, instead of providing direct access to the actual frame buffer, it returns a pointer to a shadow surface. This shadow surface allows older games that do not provide support for the capability bits to run on newer devices that use different display layouts. In either case, GAPI provides no basic two-dimensional or three-dimensional drawing abilities. GAPI and GDI do not always work well together. To guarantee the desired results, you must implement all drawing, or perform two-dimensional drawing to an off-screen device context by using GDI, and then copy the result to the frame buffer that GAPI provides.

GDI

GDI provides you with the ability to create general graphical output to be displayed onscreen. GDI functions allow you to draw lines, curves, closed figures, text, and bitmap images, which can all be applied to games. GDI greatly makes use of Windows messages for determining when and what the application needs to redraw onscreen. You need to implement any three-dimensional drawing that uses GDI in the software. GDI does not provide direct access to the frame buffer, so page flipping and back buffer drawing are somewhat irrelevant to exclusive GDI development.

DirectDraw

DirectDraw provides support for hardware-accelerated two-dimensional graphics manipulation. It retains compatibility with GDI, so you can continue to use GDI to perform any drawing that GDI supports. DirectDraw supports blitting, page flipping on supported devices, back buffers, overlays, and some alpha blending. DirectDraw allows windowed or full-screen access in addition to exclusive access mode to the video frame buffer. You need to implement any three-dimensional drawing that is to be used in DirectDraw in the software. The DirectDraw implementation in Windows Mobile 5.0 is based on the IDirectDraw4 interface on the desktop computer, and the header files are based on the header files from DirectX 6. You should check any features that are defined in the device capability bits prior to using them in DirectDraw. For example, page flipping may or may not be supported on some devices, but you can obtain that information by querying the device capabilities.

Direct3D Mobile

Direct3D Mobile provides hardware-accelerated three-dimensional graphics manipulation independent of the device. It contains support for back buffers, page flipping, and blitting. Direct3D Mobile allows windowed and full-screen access, and it does not allow direct access to the frame buffer. Drawing typically occurs to the back buffer, so the lack of direct access to the frame buffer is not a problem. Direct3D Mobile is compatible with GDI—provided that the driver allows the surface to be locked. The device capability bits indicate if the driver allows this feature. You should check the device capability bits before using any driver optional features in Direct3D Mobile.

Direct3D Mobile also provides support for three-dimensional features such as rendering polygons, shading, texturing, transformations, clipping, and lighting. Figure 1 shows a screenshot from the Direct3D Mobile sample application (which is included in this article's download code sample) that demonstrates many of these features. Direct3D Mobile compares most closely to the Direct3D 8 implementation from the Windows desktop computer, but it also contains some elements of Direct3D 9.

Figure 1. An example screenshot from the Direct3D Mobile sample application. Click the thumbnail for a larger image.

Direct3D Mobile Compared to Direct3D for Desktop Computers That Run Windows

Direct3D Mobile and Direct3D for desktop computers have a lot of common aspects. Because Direct3D Mobile has been developed specifically for use on Windows Mobile powered devices, there are a number of features that make Direct3D Mobile different from Direct3D for desktop computers. This section examines these differences and similarities.

Direct3D Mobile Runtime

The Direct3D Mobile runtime was specifically designed to be much smaller and lightweight than its desktop computer counterpart. In fact, the Direct3D Mobile runtime is about 36 KB on a mobile device that runs Windows CE and has an x86 CPU compared to roughly 1.7 MB on the desktop computer. To accomplish such a significant size reduction, the runtime's responsibilities are very minimal. The runtime contains no fallback for missing driver functionality. The driver must implement this functionality itself. The Direct3D Mobile runtime's responsibility is to validate parameters, package data to send to the driver, and cache some state information so all of the calls to the Get methods succeed. It also handles interaction with the Graphics, Windowing, and Events Subsystem (GWES) module because the drivers are not aware of anything about Windows or device contexts.

Direct3D Mobile Drivers

There are no minimum requirements for a display-based platform for it to support a Direct3D Mobile driver because Direct3D Mobile allows developers to implement fully software-based drivers. The more graphics hardware the driver can make use of, the higher the performance on the device. A graphics adapter that can support texture and lighting operations in the hardware yields the best performance.

Direct3D Mobile drivers must support the capabilities defined in the base device profile. These capabilities include correct drawing of all primitive types, support for direct and index buffer accesses, 16 and 32 bit indices, flat shading and all fill modes, scene presentation by means of swap effect, back buffers, hidden surface removal, image surfaces, vertex buffers, index buffers, world transformations, view transformations, projection transformations, some lighting, all possible flexible vertex format combinations, and several other requirements.

The Direct3D Mobile device provides the GetDeviceCaps method so the application can query what capabilities the device exposes. The GetDevicesCaps method returns the D3DMCAPS structure, which contains several sets of flags that indicate the device's abilities to perform certain operations. Before using any of the abilities that may or may not be present on a given device, the application should check the capability bits and respond appropriately if the device is or is not capable of the specified ability. These capability flags indicate driver properties such as preference for floating point or fixed point values, texturing abilities, stenciling capabilities, video buffer memory allocation abilities, blending abilities, and many others.

Common Features

Because Direct3D Mobile is based on its desktop computer counterpart, a number of features are nearly identical. These features include support for z-buffers and w-buffers, flat and Gouraud shading, multiple light sources, material and texture support including mip-maps, transformation and clipping, hardware independence, page-flipping, and several more. As this article previously mentioned, it is important to query the device capabilities to verify that any optional features are supported prior to using them.

Direct3D Mobile Limitations

There a number of features in Direct3D for desktop computers that are not supported—or only partially supported—in Direct3D Mobile. This section discusses the most significant of these features.

Unsupported Features

There are several features in the desktop computer version of Direct3D that are not included in Direct3D Mobile. For desktop computer and console developers, the most notable missing feature is the absence of support for pixel and vertex shaders. Shaders allow developers to write code to customize the pixel or vertex output to be whatever they want—as opposed to having them rely on the capabilities that the fixed function pipeline provides. Shaders have become very popular in recent years now that many desktop computer graphics cards and gaming consoles support them in the hardware. Emulating shaders in software would be a very processor-intensive operation that would be unsuitable for today's mobile devices.

Other unsupported features in Direct3D Mobile include bump mapping, cube maps, edge antialiasing, emissive materials, Phong shading, point sprites, spot lights, vertex blending, and several others. Most of these features are missing because they are not currently available in the hardware on the current generation of mobile devices, and because the goal is to keep the Direct3D Mobile runtime small.

Vertex Streams

Direct3D Mobile only supports a single vertex stream. The device always uses stream zero no matter what the value that is passed to the IDirect3DMobileDevice::SetStreamSource method indicates. Fortunately, this should not be a problem for mobile developers. The primary reason to use multiple stream sources is when you use shaders, but shaders are not supported in Direct3D Mobile, so this limitation should have minimal affect on development.

Direct3D Mobile Device

Only a single device can be instantiated at any one time when you use Direct3D Mobile; therefore, you can only have one Direct3D Mobile application running at any time. The single device instance limitation makes error checking on startup very important. If the creation of the Direct3D Mobile device fails, the application should notify the user and close the application. The single device instance limitation also makes application shutdown very important. Typically, Windows Mobile applications don't close immediately; they are switched to running in the background. This is bad for Direct3D Mobile applications because the user might not be aware that another Direct3D Mobile application is already running. It is important that when a user decides to close a Direct3D Mobile application, the application actually closes and it does not continue to run in the background, which would prevent other Direct3D Mobile applications from running until it closes.

Currently, Direct3D Mobile does not support rendering while the screen is rotated. However, your application does not need to close when it detects that the screen is rotated because the application can safely recover if the screen is rotated back to the device's default orientation. The application must reset the Direct3D Mobile device after the rotation back to the default orientation for rendering to resume properly. The need to reset the Direct3D Mobile device is analogous to a lost device in the desktop computer world of Direct3D. However, the TestCooperativeLevel function that is typically used to detect the need for a device reset in the desktop computer version is not implemented in Direct3D Mobile. Because this function is not implemented, applications should reset the device when receiving a WM_SETTINGCHANGE or WM_SIZE message. The operating system sends the WM_SETTINGCHANGE message to the application whenever the screen orientation changes. An example of detecting the current screen orientation is provided later in this article.

Note The limitation of Direct 3D Mobile only functioning properly when a device's display is set to the device's default orientation was due to a bug in the Direct 3D Mobile implementation in the original release to manufacturer (RTM) release of Windows Mobile 5.0 (version 5.1.1700, builds 14334 through 14397—not including 14334.2). This problem is corrected in later releases of Windows Mobile 5.0 (Windows Mobile 5.0 Adaptation Kit Update version 5.1.1700 build 14334.2 and later).

Device Types

By default, Direct3D Mobile only supports the hardware abstraction layer (HAL) device, which is the driver built to take full advantage of the hardware and give the best performance increases. However, by using a call to the IDirect3DMobile::RegisterSoftwareDevice method, you can use a driver written wholly in software. It can be very useful during development to register the reference implementation software driver to test all feature configurations, but typically a released application always uses the HAL device because of its performance optimizations.

Differences in Direct3D Mobile and Direct3D for Desktop Computers

As discussed previously in this article, there are many features of Direct3D for desktop computers that are also available in Direct3DMobile. This section discusses features that are common to both APIs and that have variations in their usage.

Renamed Structures, Functions, Interfaces, and Enumerations

Many of the structures, functions, interfaces, and enumerations have been renamed from the desktop computer version of Direct3D. This renaming happened for a few reasons. Direct3D Mobile is primarily based on Direct3D 8, but some of the features and changes from Direct3D 9 are also included. These additions make it inappropriate to call it either Direct3D 8 or Direct3D 9 because it really is neither, and developers would have compatibility issues if they brought code over from either desktop computer version of Direct3D. For the most part, the Direct3D Mobile APIs behave identically to their desktop counterparts wherever possible. Primarily, they differ when an API can take either fixed or floating-point data, which is discussed in the next section of this article.

Renaming also establishes Direct3D Mobile as an API that is targeted specifically for mobile devices. Keep in mind that Direct3D Mobile was developed and optimized specifically for use on mobile devices even though it is based on its desktop computer counterpart, so the name change is appropriate.

You can easily deal with the naming convention when porting desktop computer code to Windows Mobile powered devices. Structures, functions, interfaces, and enumerations have all been renamed in a consistent format that should make it fairly easy to convert to the Direct3D Mobile naming convention. Table 1 lists how the naming convention for Direct3D for desktop computers relates to the Direct3D Mobile naming convention.

Table 1. Name prefixes for structures, functions, interfaces, and enumerations for Direct3D and Direct3D Mobile

Direct3D for desktop computers Direct3D Mobile
D3D D3DM
Direct3D Direct3DMobile
IDirect3D IDirect3DMobile

Fixed Point Support

Floating point calculations can be very inefficient and time consuming on a processor that does not have native support for floating point math. Floating point operations can easily take thousands of cycles to complete without native support. The extra processing cycles could be catastrophic to the performance of a game that tries to render thousands of polygons every frame.

Thankfully, Direct3D Mobile adds fixed point format support throughout the graphics pipeline to combat the performance problem. The fixed point format that Direct3D Mobile uses is 16.16, that is, 16 bits are used for the whole part of the number, and 16 bits are used for the fractional part. Custom vertex formats now allow fixed point values to represent position, normals, and texture coordinates. You can also perform transformations and lighting by using fixed point values. The device capabilities report if the device prefers to receive values as floating point or fixed point values. If the device prefers floating point values and it is passed a fixed point value or if the device prefers fixed point values and it is passed a floating point value, the driver must convert it internally before using the data. So, it is more efficient to work and pass data to the driver in its preferred format.

The D3DMVALUE type is defined as an int in Direct3D Mobile, but it is defined as a float in the desktop computer version of Direct3D. This definition supports the option of both fixed point and floating point values in Direct3D Mobile. If developers are unaware of this difference, it could cause issues in the code when porting from the desktop computer to a Windows Mobile powered device. The D3DM_MAKE_D3DMVALUE functions are provided as a convenience for converting various types to be properly stored as a D3DMVALUE type.

Enumerated Values

Direct3D Mobile has fewer enumerated values than the desktop computer Direct3D implementation. The lightweight design of Direct3D Mobile limits the enumeration values to those that correspond to the available features in Direct3D Mobile. For example, the D3DMRS_SHADEMODE type only provides enumerated values for the flat and Gouraud shading modes. There are other enumerated types that have extra values. For example, D3DMFORMAT contains extra enumerated values to support the option of fixed point or floating point values, and the D3DMFVF_ values have been changed to support both fixed point and floating point values.

Native Code Development

This section uses the native code sample application to explore various aspects of Direct3D Mobile development. The content focuses on areas that are specific to the mobile implementation of Direct3D. For more information, see the native code sample application in this article's download code sample.

Required Headers and Libraries

Table 2 lists a summary of the header files for Direct3D Mobile and DirectDraw.

Table 2. Direct3D Mobile and DirectDraw header files

File name Description
d3dm.h Contains the interface definitions for each of the Direct3D Mobile interfaces. Also contains some error code definitions.
d3dmtypes.h Contains the various structures, enumerations, and constant definitions that are used throughout Direct3D Mobile.
d3dmcaps.h Contains the definition of the Direct3D Mobile capabilities structure and the constants that apply to it.
d3dmx.h Includes the other Direct3D Mobile extension (D3DMX) related header files.
d3dmxmath.h Contains some macros for converting between fixed and floating point values. Also contains some helper structures for manipulating vectors and matrices that contain fixed or floating point values.
d3dmxtex.h Contains helper functions for use in loading surfaces and textures.
d3dmxerr.h Contains error codes used in the D3DMX–elated functions.
ddraw.h Contains DirectDraw related interfaces, structures, enumerations, and constants.
ddstream.h Contains DirectDraw stream related interfaces.
ddmm.h Contains functions needed for DirectDraw multiple monitor support.

Table 3 lists a summary of the library files for Direct3D Mobile and DirectDraw.

Table 3. Direct3D Mobile and DirectDraw library files

File name Description
d3dm.lib Contains the implementation of Direct3D Mobile functions.
d3dmx.lib Contains the implementation of Direct3D Mobile extension functions.
ddraw.lib Contains the implementation of DirectDraw functions.

Direct3D Mobile Device Creation

As previously mentioned in this article, Direct3D Mobile currently supports only the 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. The following code example creates the default device.

// Set up the parameters for creating the Direct3D Mobile device
D3DMPRESENT_PARAMETERS sPresentParameters; 
memset(&sPresentParameters, 0, sizeof(sPresentParameters));

//Currently using a window
sPresentParameters.Windowed = TRUE;

//Discard is the most efficient swap method
sPresentParameters.SwapEffect = D3DMSWAPEFFECT_DISCARD;

//Use the back buffer format that matches the current
//desktop display format
sPresentParameters.BackBufferFormat = D3DMFMT_UNKNOWN;

//Use a 16-bit depth buffer
sPresentParameters.EnableAutoDepthStencil = TRUE;
sPresentParameters.AutoDepthStencilFormat = D3DMFMT_D16;

//Create the Direct3D Mobile device
HRESULT nResult = g_pD3DM->CreateDevice(D3DMADAPTER_DEFAULT, D3DMDEVTYPE_DEFAULT, hWnd, 0, &sPresentParameters, &g_pD3DMDevice);

Device Capabilities Discovery

Whenever you use the optional features of Direct3D Mobile, be sure to check the device capabilities bits to see if the device can handle what the application wants it to do. One of the primary capabilities to check for is whether the driver prefers data to be in a fixed or floating point format. Giving the data to the driver in the preferred format can really boost an application's performance.

You should also check several other capabilities to ensure that your application is running at its top performance. By storing vertex buffers and textures in video memory, an application can also improve its performance; therefore, the application should check if these features are supported. If your device has better lighting support, you can check the types of lighting available through the driver to provide a more rewarding visual experience. The documentation lists many other capabilities bits that you can and should check to make the most use of the features available.

The following code example retrieves and checks if the driver prefers floating point values and if the device supports directional lights.

D3DMCAPS g_sDeviceCaps;
//Get the device capabilities, so you can see what the driver can do
HRESULT eResult = g_pD3DMDevice->GetDeviceCaps(&g_sDeviceCaps);
if(FAILED(eResult)) return eResult;
// ...

// Check if the device prefers floating point values
if(g_sDeviceCaps.DevCaps & D3DMDEVCAPS_NATIVEFLOAT)
{
// ...
}

// Check if the device supports directional lights
if(g_sDeviceCaps.VertexProcessingCaps & D3DMVTXPCAPS_DIRECTIONALLIGHTS)
{
// ...
}

Vertex Buffer Data Creation in the Fixed Point Format or the Floating Point Format

As previously mentioned in this article, it is better to provide data to the driver in its preferred format. The following code example is a condensed version from the sample application. The code demonstrates how to create vertex buffer data in the preferred format. The example defines and uses a custom vertex that can store either fixed point or floating point data. The code uses values of the D3DMVALUE type that abstract whether a floating point or fixed point value is stored. The D3DM_MAKE_D3DMVALUE function enables the storing of floating point values as a D3DMVALUE, and the D3DMXToFixed macro allows storing fixed point values as a D3DMVALUE.

//Custom type that represents the vertices
struct SCustomVertex
{
    //The vertex position
    D3DMVECTOR m_sPosition;

    //The vertex normal
    D3DMVECTOR m_sNormal;

    //The texture coordinates
    D3DMVALUE m_fU, m_fV;
};
// ...
//Check if the device prefers floating point or fixed point values
if(g_sDeviceCaps.DevCaps & D3DMDEVCAPS_NATIVEFLOAT)
{
    g_bFloatingPointPreferred = TRUE;
}
else
{
    g_bFloatingPointPreferred = FALSE;
}
// ...
//Determine if you're using floating or fixed point values
//in the buffer
DWORD nFVF;
if(g_bFloatingPointPreferred)
{
    nFVF = D3DMFVF_XYZ_FLOAT |
           D3DMFVF_NORMAL_FLOAT |
           D3DMFVF_TEX1 |
           D3DMFVF_TEXCOORDSIZE2(0) |
           D3DMFVF_TEXCOORDFLOAT(0);
}
else
{
    nFVF = D3DMFVF_XYZ_FIXED |
           D3DMFVF_NORMAL_FIXED |
           D3DMFVF_TEX1 |
           D3DMFVF_TEXCOORDSIZE2(0) |
           D3DMFVF_TEXCOORDFIXED(0);
}

//Create the vertex buffer. You'll put 36 of the vertices
//into the buffer. (6 die faces * 2 tris per face * 3 verts per tri)
eResult = g_pD3DMDevice->CreateVertexBuffer(36*sizeof(SCustomVertex),
                                                0,
                                                nFVF,
                                                eD3DMPool,
                                                &g_pVB);
// ...
//Lock the vertex buffer so you can write data to it
SCustomVertex *pVertices;
eResult = g_pVB->Lock(0, 0, (void**)&pVertices, 0);
// ...
//Build the vertex data for the die based on the preferred format
D3DMVALUE c_fOne;
D3DMVALUE c_fMinusOne;
D3DMVALUE c_fZero;
D3DMVALUE c_fOneDivThree;
D3DMVALUE c_fTwoDivThree;

//Precompute the values being used so they don't have to be 
//recalculated several times

//Use floating point values if preferred
if(g_bFloatingPointPreferred)
{
    c_fOne = D3DM_MAKE_D3DMVALUE(1.0f);
    c_fMinusOne = D3DM_MAKE_D3DMVALUE(-1.0f);
    c_fZero = D3DM_MAKE_D3DMVALUE(0.0f);
    c_fOneDivThree = D3DM_MAKE_D3DMVALUE(1.0f/3.0f);
    c_fTwoDivThree = D3DM_MAKE_D3DMVALUE(2.0f/3.0f);
}
//Otherwise, use fixed point values
else
{
    c_fOne = D3DMXToFixed(1.0f);
    c_fMinusOne = D3DMXToFixed(-1.0f);
    c_fZero = D3DMXToFixed(0.0f);
    c_fOneDivThree = D3DMXToFixed(1.0f/3.0f);
    c_fTwoDivThree = D3DMXToFixed(2.0f/3.0f);
}

//Set up the data
SCustomVertex asVertexData[] = 
{
    //Face 1
    {c_fOne, c_fMinusOne, c_fOne, 
    c_fZero, c_fMinusOne, c_fZero, 
    c_fOneDivThree, c_fOneDivThree},
    {c_fMinusOne, c_fMinusOne, c_fOne, 
    c_fZero, c_fMinusOne, c_fZero, 
    c_fZero, c_fOneDivThree},
    // ...
}
//Copy the data to the vertex buffer
memcpy(pVertices, asVertexData, sizeof(asVertexData));

//Unlock the buffer now that you're done with the data
g_pVB->Unlock();

The preceding code example first defines a SCustomVertex structure as a custom vertex type that contains position, normal, and texture coordinate data. Because the values are stored with a type of D3DMVALUE, either floating point or fixed point data can be stored for each vertex. Following the structure definition, the code checks the device capabilities to determine if the device prefers values to be in a fixed point or floating point format. The code then determines the vertex format of the data that is specified in the vertex buffer based on the device's floating point preference. The code uses this vertex format to create a vertex buffer large enough to store the data. The code then locks the vertex buffer in preparation for copying the data into it. Based on the device's floating point preference, the code builds the common values to be stored in the vertex data. This technique prevents the conversion from floating point to fixed point format from occurring multiple times for the same values, and it also allows you to define each vertex in the vertex array in one place in the code because of the abstraction the D3DMVALUE type provides. After the code builds the array of vertices, it is then copied to the vertex buffer, and the vertex buffer is unlocked because data is no longer being written to it.

Multiple Resolutions and Screen Orientations

Windows Mobile supports several different screen resolutions and orientations. Table 4 lists the screen resolutions and orientations that are currently supported.

Table 4. Screen resolutions and orientations that Windows Mobile supports

Orientation Supported resolutions
Portrait (Pocket PC) 240 x 320 and 480 x 640
Portrait (Smartphone) 176 x 220 and 240 x 320
Landscape (Pocket PC) 320 x 240 and 640 x 480
Landscape (Smartphone) 320 x 240
Square (Pocket PC) 240 x 240 and 480 x 480

You need to ensure that you handle each resolution and orientation appropriately. For example, a three-dimensional application needs the camera aspect ratio to match that of the current screen resolution, which prevents any sort of distortion when drawing objects. But in a two-dimensional application, the application may want to resize or re-layout components based on the current screen resolution or orientation. How you handle each of these scenarios depends on your specific application needs. For more information about designing your game to deal with multiple resolutions and screen orientations, see Porting GAPI Games from Portrait to Square Screens and Are You Square Aware?.

You can obtain the current screen resolution by using the GetSystemMetrics function. Direct3D does not currently support using rotated screens. So, the sample application detects a rotated screen and pauses rendering until the user rotates the screen to the default orientation. The application should check for screen rotation during startup and whenever a WM_SETTINGCHANGE or WM_SIZE message is received.

Note As noted previously in this article, the limitation of Direct 3D Mobile only functioning properly when a device's display is set to the device's default orientation was due to a bug in the Direct 3D Mobile implementation in the original RTM release of Windows Mobile 5.0 (version 5.1.1700 builds 14334 through 14397—not including 14334.2). This problem is corrected in later releases of Windows Mobile 5.0 (Windows Mobile 5.0 AKU, version 5.1.1700 build 14334.2 and later).

The following code example determines the screen rotation.

//Get the current device settings
DEVMODE sDevMode = {0};
sDevMode.dmSize = sizeof(DEVMODE);
sDevMode.dmFields = DM_DISPLAYORIENTATION;

//Even though passing CDS_TEST generally means to test if the specified
//settings are supported, it is also used for querying the current
//display orientation
ChangeDisplaySettingsEx(NULL, &sDevMode, NULL, CDS_TEST, NULL);

//Check if the screen orientation is rotated
if(sDevMode.dmDisplayOrientation != DMDO_0)
{
// ...
}

Input

Though handling input is not directly related to Direct3D Mobile, it is related to GAPI. GAPI provides the ability for the application to receive button messages without the messages being sent to the shell, thus providing the application with full exclusive access to the hardware buttons. GAPI allows you to enable this ability by calling the GXOpenInput method and to disable this feature by calling the GXCloseInput method. You should be certain that applications that use this ability call the GXSuspend method when they are losing focus and that they call the GXResume method when they are gaining focus to ensure that the application is not filtering input when it is not active. GAPI also provides the GXGetDefaultKeys function, which provides the application a set of key codes defined as the best buttons for games. For more information, see the GAPI overview documentation.

Windows messages send GAPI input to the application. The following code example illustrates responding to the up direction key when the user presses it.

//Key list of preferred input for the application
GXKeyList g_sKeyList;
// ...
//Get the default keys to use
g_sKeyList = GXGetDefaultKeys(GX_NORMALKEYS);
// ...
//Process any Windows messages
LRESULT WINAPI MsgProc(HWND hWnd, 
                       UINT msg, 
                       WPARAM wParam, 
                       LPARAM lParam)
{
    switch(msg)
    {
        case WM_KEYDOWN:
            if (g_sKeyList.vkUp == wParam)
            {
                //Do some action based on the up direction key
            }
            break;
        case WM_ACTIVATE:
            if(WA_INACTIVE == LOWORD(wParam))
            {
                //Pause GAPI's unfiltered button access
                GXSuspend();
            }
            else
            {
                //Unpause GAPI's unfiltered button access
                GXResume();
            }
            break;
    }
    return DefWindowProc( hWnd, msg, wParam, lParam );
}

Developers who design applications to run on both Smartphones and Pocket PCs need to be sure that the applications respond to all of the required input for both device types. All Pocket PCs have stylus input support and hardware direction keys, and some have a full QWERTY keyboard. All Smartphones have hardware direction keys, no stylus support, and at least a numeric pad; some Smartphones will have a full QWERTY keyboard.

Although it is common in desktop computer games to use multiple concurrent key presses, such as SHIFT and the direction keys to run in a first-person shooter game, using multiple concurrent button presses is not supported on Windows Mobile powered devices; therefore, you should design your game's controls to avoid the need for concurrent button presses.

Other Graphics Features in Windows Mobile That May Be Useful

Windows Mobile 5.0 has some new features that may be useful or innovative to use in games. The SHCameraCapture function makes it easy for an application to access the camera on the device. The GetOpenFileNameEx function makes it easy to allow users to choose image files that are stored on their mobile devices. You can use functions like these to allow users to customize their gaming experiences. For example, if a game allows online play, it could allow users to choose an existing image file or take a photo using the device's camera that they can use as their avatar in the game, or you may want to allow users to create new textures that they can replace in the game.

The following code opens the My Pictures folder and allows users to choose an existing bitmap to use as a texture. There are several flags you may specify when opening the image selection dialog to control its behavior. This code example uses the following flags:

  • OFN_EXFLAG_THUMBNAILVIEW flag specifies that the view should show thumbnails of the image files.
  • OFN_EXFLAG_HIDEDRMPROTECTED flag specifies that digital rights management (DRM) protected files are not displayed.
  • OFN_EXFLAG_NOFILECREATE flag specifies that the user cannot create new image files by using the device's camera.
  • OFN_EXFLAG_LOCKDIRECTORY flag indicates that the user cannot navigate to another directory to choose an image file.
OPENFILENAMEEX sOpenFileNameEx = {0};
TCHAR    szFile[MAX_PATH];
TCHAR    szInitialDir[MAX_PATH];

//Get the path to the My Pictures folder
if(SHGetSpecialFolderPath(g_hWnd, 
                          szInitialDir, 
                          CSIDL_MYPICTURES, 
                          FALSE))
{
    //Build the parameters to the GetOpenFileNameEx function dialog
    sOpenFileNameEx.lStructSize = sizeof(sOpenFileNameEx);
    sOpenFileNameEx.hwndOwner = g_hWnd;
    sOpenFileNameEx.hInstance = g_hInstance;
    sOpenFileNameEx.lpstrFile = szFile;
    sOpenFileNameEx.nMaxFile = sizeof(szFile)/sizeof(szFile[0]);
    sOpenFileNameEx.lpstrFilter = 
                        TEXT("Bitmap Files (*.bmp)\0*.bmp\0");
    sOpenFileNameEx.lpstrTitle = TEXT("Choose Dice Bitmap");
    sOpenFileNameEx.lpstrInitialDir = szInitialDir;
    sOpenFileNameEx.ExFlags = OFN_EXFLAG_THUMBNAILVIEW | 
                                OFN_EXFLAG_HIDEDRMPROTECTED | 
                                OFN_EXFLAG_NOFILECREATE | 
                                OFN_EXFLAG_LOCKDIRECTORY;
    //Show the dialog to choose an image file
    if(GetOpenFileNameEx(&sOpenFileNameEx))
    {
        //The user chose a file, so try to load it as a texture
        eInitTextures(sOpenFileNameEx.lpstrFile);
    }
}

Managed Code Development

The managed code sample application explores various aspects of Direct3D Mobile development. This section discusses the same topics as the Developing with Native Code section, but it examines them from a managed perspective and focuses on differences from the native code section. For more information, see the managed code sample application in this article's download code sample.

Required Assemblies

You must add a reference to the Microsoft.WindowsMobile.DirectX.dll assembly when you develop a DirectX application in managed code. Classes that are located in the "Microsoft.WindowsMobile.DirectX" and "Microsoft.WindowsMobile.DirectX.Direct3D" namespaces provide you access to the required DirectX functionality.

Direct3D Mobile Device Creation

You can create a Direct3D Mobile device by using the following code example. The same device restrictions apply as the native code example.

//Set up the parameters for creating the Direct3D Mobile device
PresentParameters sPresentParameters = new PresentParameters();
sPresentParameters.Windowed = true;
sPresentParameters.SwapEffect = SwapEffect.Discard;
sPresentParameters.BackBufferFormat = Format.Unknown;
sPresentParameters.EnableAutoDepthStencil = true;
sPresentParameters.AutoDepthStencilFormat = DepthFormat.D16;

//Create the Direct3D Mobile device
m_oDevice = new Device(0, 
                    DeviceType.Default, 
                    this, 
                    CreateFlags.None, 
                    sPresentParameters);

Device Capabilities Discovery

When you use managed code, each of the device capabilities are mapped to properties (in native code, you have to check bitmasks). The following code example retrieves and checks if the device prefers floating point values and checks if the device supports directional lights.

Caps m_sDeviceCaps;
//Get the device capabilities, so you can use them later
m_sDeviceCaps = m_oDevice.DeviceCaps;
//Check if the device prefers floating point values
if(m_sDeviceCaps.DeviceCaps.NativeFloat)
{
// ...
}

//Check if the device supports directional lights
if(m_sDeviceCaps.VertexProcessingCaps.SupportsDirectionalLights)
{
// ...
}

Vertex Buffer Data Creation in the Fixed Point Format or the Floating Point Format

When using native code, you must define all custom vertex types. In managed code, several predefined custom vertex types are available through the Microsoft.WindowsMobile.DirectX.Direct3D.CustomVertex class. You may still create a custom vertex types as necessary—as is the case with the following code example. When the device prefers fixed point values, the code example defines a custom vertex type that uses fixed point position, normals, and texture coordinates. However, when the device prefers floating point values, the code example uses the predefined custom vertex type CustomVertex.PositionNormalTextured.

//Custom type that represents the fixed point vertices
[StructLayout(LayoutKind.Sequential)]
public struct SCustomVertex
{
public static readonly VertexFormats Format =
                            VertexFormats.PositionFixed |
                            VertexFormats.NormalFixed | 
                            VertexFormats.Texture1 | 
                            VertexTextureCoordinate.Size2() | 
                            VertexTextureCoordinate.Fixed(0);
public Vector3Fixed m_sPosition;
public Vector3Fixed m_sNormal;
public FixedPoint m_fU;
public FixedPoint m_fV;
    // ...
}
//Check if the device prefers floating point
g_bFloatingPointPreferred = m_sDeviceCaps.DeviceCaps.NativeFloat;

//Determine if you're using floating or fixed point values
//in the buffer
Type oVertexType;
VertexFormats eVertexFormat;
if (m_bFloatingPointPreferred)
{
    oVertexType = typeof(CustomVertex.PositionNormalTextured);
    eVertexFormat = CustomVertex.PositionNormalTextured.Format;
}
else
{
    oVertexType = typeof(SCustomVertex);
    eVertexFormat = SCustomVertex.Format;
}

//Create the vertex buffer. You'll put 36 of the vertices
//into the buffer. (6 die faces * 2 tris per face * 3 verts per tri)
m_oVB = new VertexBuffer(oVertexType, 36, m_oDevice, Usage.None, 
                            eVertexFormat, eD3DMPool);
// ...
float c_fOne = 1.0f;
float c_fMinusOne = -1.0f;
float c_fZero = 0.0f;
float c_fOneDivThree = 1.0f / 3.0f;
float c_fTwoDivThree = 2.0f / 3.0f;

if (m_bFloatingPointPreferred)
{
    CustomVertex.PositionNormalTextured[] aoVertices =
    {
    //Face 1
    new CustomVertex.PositionNormalTextured(
                                    c_fOne, c_fMinusOne, c_fOne, 
                                    c_fZero, c_fMinusOne, c_fZero, 
                                    c_fOneDivThree, c_fOneDivThree),
    new CustomVertex.PositionNormalTextured(
                                        c_fMinusOne,c_fMinusOne,c_fOne, 
                                        c_fZero, c_fMinusOne, c_fZero, 
                                        c_fZero, c_fOneDivThree),
    // ...
    }

    //Set the vertex data
    m_oVB.SetData(aoVertices, 0, LockFlags.None);
}
else
{
    SCustomVertex[] aoVertices =
    {
    //Face 1
    new SCustomVertex(c_fOne, c_fMinusOne, c_fOne, 
                    c_fZero, c_fMinusOne, c_fZero, 
                    c_fOneDivThree, c_fOneDivThree),
    new SCustomVertex(c_fMinusOne,c_fMinusOne,c_fOne, 
                    c_fZero, c_fMinusOne, c_fZero, 
                    c_fZero, c_fOneDivThree),
    // ...
    }
    //Set the vertex data
    m_oVB.SetData(aoVertices, 0, LockFlags.None);
}

The preceding code example first defines a SCustomVertex structure as a custom vertex type that contains fixed point position, normal, and texture coordinate data. Following the structure definition, the code checks the device capabilities to determine if the device prefers values to be in a fixed point or floating point format. The code then determines the vertex format of the data that is specified in the vertex buffer based on the device's floating point preference by using the newly defined SCustomVertex structure if the device prefers the fixed point format or by using the predefined CustomVertex.PositionNormalTextured structure if the device prefers the floating point format. The code uses this vertex format to create a vertex buffer large enough to store the data. The code then builds the data using the appropriate custom vertex type and copies the data into the vertex buffer.

Multiple Resolutions and Screen Orientations

You can obtain the current screen working area by using the Screen.PrimaryScreen.WorkingArea property, which contains a rectangle that represents the working area of the screen or by using the Screen.PrimaryScreen.Bounds property, which contains a rectangle that represents the entire screen size. The application should check the current screen orientation on application startup and when the Resize event of the form is raised. The following code example checks the screen rotation.

//If the screen orientation is rotated at all, notify the user
if (SystemSettings.ScreenOrientation != ScreenOrientation.Angle0)
{
    // Display a message that says the screen is rotated
    // ...
} 

Input

The concerns and considerations for handling input are identical to those in the native code example; however, there is no provided managed wrapper to the native GAPI functions that you can use for giving your application exclusive access to the hardware buttons. To use these features from within managed code, you must either directly invoke the GAPI DLL entry points by using platform invoke or write a custom managed wrapper for the GAPI functions. Using platform invoke and writing a custom wrapper for GAPI are outside the scope of this article and are large topics that other papers cover. For more information about these topics, see An Introduction to P/Invoke and Marshaling on the Microsoft .NET Compact Framework and Dancing Rectangles: Using GAPI to Create a Managed Graphics Library. If your application does not require the services of GAPI, you can easily respond to input through events such as MouseUp, MouseDown, MouseMove, KeyUp, and KeyDown. The following code example directly invokes GAPI DLL entry points from managed code.

// Provide access to the GAPI functions. The GAPI functions are not
// exported with the extern C declaration, so the exported names are
// mangled. You need to access them by entry point index.
[DllImport("gx.dll", EntryPoint = "#10")]
extern public static int GXResume();

[DllImport("gx.dll", EntryPoint = "#12")]
extern public static int GXSuspend();

private void D3DForm_Activated(object sender, EventArgs e)
{
    //Unpause GAPI's unfiltered button access
    GXResume();
}
private void D3DForm_Deactivate(object sender, EventArgs e)
{
    //Pause GAPI's unfiltered button access
    GXSuspend();        
}

Other Graphics Features in Windows Mobile That May Be Useful

The same camera features and picture choosing functionality that are available in native code are also exposed in managed code. The Microsoft.WindowsMobile.Forms.CameraCaptureDialog class exposes the camera functionality. The Microsoft.WindowsMobile.Forms.SelectPictureDialog class exposes the picture choosing functionality. The following code example uses the SelectPictureDialog class. Just as in the native example, the code opens the My Pictures folder and allows users to choose an existing bitmap to use as a texture. There a several flags that you can set when opening the image selection dialog such as whether DRM protected files are shown, if the user can take a photo, and if the user can navigate to another directory to choose an image file.

//Build the parameters to the SelectPictureDialog
SelectPictureDialog oPictureDialog = new SelectPictureDialog();
oPictureDialog.Owner = this;            
oPictureDialog.Filter = "Bitmap Files (*.bmp)\0*.bmp\0";
oPictureDialog.Title = "Choose Dice Bitmap";
oPictureDialog.InitialDirectory = null;
oPictureDialog.CameraAccess = false;
oPictureDialog.LockDirectory = true;
oPictureDialog.ShowDrmContent = false;
            
//Display the dialog
if(DialogResult.OK == oPictureDialog.ShowDialog())
{
    //The user chose a file, so try to load it as a texture
    bInitTextures(oPictureDialog.FileName);
}

Conclusion

The availability of Direct3D Mobile and DirectDraw on Windows Mobile 5.0 powered devices makes it very easy for developers who are used to using the desktop computer, Microsoft Xbox, or Microsoft Xbox 360 versions of DirectX to migrate to a mobile platform, and these APIs also make it much easier for current mobile developers to create games on mobile devices. Not only do these APIs provide developers the opportunity to spend more time working on the gameplay of their games and less time developing technology, but they can also provide performance improvements because Direct3D Mobile and DirectDraw can use hardware acceleration on mobile devices. Additionally, because DirectX for Windows Mobile 5.0 powered devices provides both native and managed support, developers are given even more flexibility in deciding how they want to make mobile games.