Frame Hierarchies: The RMBegin2 Sample

Creating a device, building a scene, and moving the scene are fundamental concepts in Microsoft® Direct3D® Retained Mode. The RMBegin2 sample builds on these concepts, which were introduced by the RMBegin1 sample. This article walks you through code from the RMBegin2 sample.

Similar to RMBegin1, RMBegin2 demonstrates creating a default device, loading meshes from Microsoft DirectX® files, adding color to the objects and lights to the scene, and rotating the objects. RMBegin2 builds a more complex scene than RMBegin1, scales the objects, and uses a hierarchy of frames. In the process of building the scene, the sample uses three meshbuilders, several frames, and several lights. The sample also creates a DirectDrawClipper object, viewport and camera. RMBegin2 also adds new functionality by resizing the scene when the application window is resized and by handling the WM_ACTIVATE and WM_PAINT messages.

RMBegin2 provides a hierarchy of frames with three spheres (representing a sun, planet, and moon) revolving around their respective parent frames.

The source code for the RMBegin2 sample consists of one self-contained file (RMBegin2.cpp). For simplicity, this sample doesn't use any separate header files, except for the standard Microsoft Windows®, DirectDraw®, and Direct3D Retained Mode header files. It also does not contain any menus, icons, or other resources.

This sample uses the Sphere0.x, Sphere1.x, and Sphere2.x files from the DirectX Foundation Software Development Kit (SDK) to provide the moon, planet, and sun images for the scene. By default when you install the SDK, the SDK's setup program sets an environment variable to point to the SDK's media directory. These three DirectX (.x) files reside in that directory, allowing the application to locate the file automatically. If you have not installed the SDK and simply run RMBegin2.exe, these DirectX files must be located in the same directory as RMBegin2.exe.

This article assumes you are an experienced C or C++ programmer and are familiar with beginning Windows programming concepts, such as the message loop, creating a window, and using menus.

This article contains the following sections.

Dependencies

Global Declarations

The sample defines some constants and global variables to help organize information.

The application-defined RELEASE macro releases an object only if the object is not NULL and ensures that an object is initialized to NULL after it has been released. This practice eliminates the problem of trying to release an object that has already been released, which can cause undesired results, such as page faults.

// Macro to release an object. 
#define RELEASE(x) if (x != NULL) {x->Release(); x = NULL;} 

The application-defined DISPLAYMSG macro uses the Windows MessageBox function to display information to the user. Using this macro rather than direct calls to MessageBox throughout the sample code makes the code cleaner and easier to read.

// Macro to display a message box containing the given string. 
#define DISPLAYMSG(x) MessageBox(NULL, x, "D3DRM Sample", MB_OK);

Global variables keep track of the key objects, such as the Direct3D Retained Mode and DirectDrawClipper objects (the DirectDrawClipper object restricts drawing to a designated area). The myglobs structure collects device, window device, viewport, scene, and camera information, as well as information about whether the application is minimized and whether objects have been initialized. Note that RMBegin2 adds a Direct3D Retained Mode window device to the objects RMBegin1 uses.

// Global Variables
LPDIRECT3DRM3 lpD3DRM = NULL;            // Direct3D Retained Mode object 
LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // DirectDrawClipper object 

// Global Structure
struct _myglobs {
     
   // Direct3DRM device. 
    LPDIRECT3DRMDEVICE3 dev;           

    // Direct3DRM window device. 
    LPDIRECT3DRMWINDEVICE windevice;           

    // Direct3DRM viewport through which to view the scene. 
    LPDIRECT3DRMVIEWPORT2 view;  

    // Master frame in which other frames are placed. 
    LPDIRECT3DRMFRAME3 scene;    

    // Frame describing the users point of view. 
    LPDIRECT3DRMFRAME3 camera;   

    // Application is minimized 
    BOOL bMinimized;            

    // All Direct3D Retained Mode objects have been initialized. 
    BOOL bInitialized;          

} myglobs;

Function Summaries

RMBegin2.cpp contains the following functions.

BuildScene creates the scene to be rendered. It loads meshes from DirectX files, scales the meshes, adds color to the meshes, creates a hierarchy of frames, and adds lights to the scene. BuildScene also sets the position of objects and how objects should be rotated. CreateObjects, which initializes global variables and creates default devices, frames, and viewports, uses this function. Although conceptually similar to the RMBegin1 BuildScene function, the RMBegin2 BuildScene functions creates a more complex scene.


BOOL BuildScene(LPDIRECT3DRM3, LPDIRECT3DRMDEVICE3, 
                LPDIRECT3DRMFRAME3, LPDIRECT3DRMFRAME3);

RenderScene draws the scene that BuildScene created. Each time WinMain uses this function, RenderScene moves the scene, giving the effect of a spinning object. This function is identical to the RenderScene function provided by the RMBegin1 sample.

static BOOL RenderScene(void);

AddMediaPath provides a path to the textures and .x files needed by the application by searching the registry.

VOID AddMediaPath(LPDIRECT3DRM3 pD3DRM)

CreateObjects initializes the global variables and creates a default device, window device, camera frame, scene frame, and viewport. CreateObjects uses BuildScene to create the scene to be rendered. InitApp, which creates the application's window, uses CreateObjects. This function is identical to the CreateObjects function provided by the RMBegin1 sample, except that it also creates a window device.

BOOL CreateObjects(HWND win);

InitApp provides standard, Windows application code to create the window class and the application's window. It uses CreateObjects to initialize global objects. This function is identical to the InitApp function provided by the RMBegin1 sample, except that it provides a different window caption.

static HWND InitApp(HINSTANCE, int);

WinMain provides the application's message loop. It uses InitApp to initialize the application and uses RenderScene to draw the scene. This function is identical to the WinMain function provided by the RMBegin1 sample.

int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int);

WindowProc handles messages for the application. It handles cases where the application is minimized, resized, activated, painted, or destroyed.

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

For more information about these functions, see the following topics in this article.

BuildScene

The BuildScene function creates the scene to be rendered. All Direct3D Retained Mode applications must create a scene, but the code required depends on the scene. Applications typically create frames and lights, add visuals to the scene, and set the position of objects. This function also scales objects and sets up a hierarchy of frames.

This section of this article provides code fragments from the BuildScene function and presents the steps the RMBegin2 sample performs to build the sun, planet, and moon scene. This section also presents the complete source code for the RMBegin2 sample's BuildScene function. If an error occurs, the code uses goto statements to jump to the end of the function and ensure that all objects are properly released before returning from the function.

  1. Load meshes from DirectX files by using the IDirect3DRM3::CreateMeshBuilder and IDirect3DRMMeshBuilder3::Load methods. The following code fragment loads one mesh. The sample uses a similar process to load the other meshes.
        if (FAILED(lpD3DRM->CreateMeshBuilder(&sun_builder)))
            goto generic_error;
    
        rval = sun_builder->Load("sphere2.x", NULL,
                        D3DRMLOAD_FROMFILE, NULL, NULL);
        if (FAILED(rval)) {
            DISPLAYMSG("Failed to load sphere2.x file.");
            goto ret_with_error;
        }
    
  2. Scale and color the meshes. The three spheres that the sample loads are all nearly the same size, so the sample increases the size of the sun sphere and decreases the size of the planet and moon spheres. The following code fragment shows this process for one mesh.
        if (FAILED(sun_builder->Scale(D3DVAL(1.2), D3DVAL(1.2), 
                                      D3DVAL(1.2))))
            goto generic_error;
        if (FAILED(sun_builder->SetColorRGB(D3DVAL(1.0), D3DVAL(1.0),
                                            D3DVAL(0.0))))
            goto generic_error;
    
  3. Create a hierarchy of frames within the scene by using the IDirect3DRM3::CreateFrame method. The following code fragment creates a sun frame within the master scene frame, a planet frame within the sun frame, and a moon frame within the planet frame. When the sample sets the object's rotation, this hierarchy governs which objects revolve around the other objects.
        if (FAILED(lpD3DRM->CreateFrame(scene, &sun)))
            goto generic_error;
        if (FAILED(lpD3DRM->CreateFrame(sun, &planet)))
            goto generic_error;
        if (FAILED(lpD3DRM->CreateFrame(planet, &moon)))
            goto generic_error;
    
  4. Use the meshbuilder object to convert to the underlying meshes by using IDirect3DRMMeshBuilder3::CreateMesh. This code fragment creates the sun mesh.
        if (FAILED(sun_builder->CreateMesh(&sun_mesh))) 
            goto generic_error;
    
  5. Add the loaded meshes into the appropriate child frames by using the IDirect3DRMFrame3::AddVisual method. This code fragment adds the sun mesh into the sun frame.
        if (FAILED(sun->AddVisual((LPDIRECT3DRMVISUAL) sun_mesh)))
            goto generic_error;
    
  6. Set the camera position within the scene by using the IDirect3DRMFrame3::SetPosition method. Set the other object's position, as well. This code fragment sets the camera's and sun's positions. The sun appears to be straight in front of the camera because the x-coordinate and y-coordinate for the sun position and camera position are the same (0,0).
        if (FAILED(camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), 
                                       -D3DVAL(25))))
            goto generic_error;
        if (FAILED(sun->SetPosition(scene, D3DVAL(0), D3DVAL(0), 
                                    D3DVAL(0))))
            goto generic_error;
    
  7. Set the frame's rotation by using the IDirect3DRMFrame3::SetRotation method. This fragment sets the sun's rotation.
        if (FAILED(sun->SetRotation(scene, D3DVAL(0), D3DVAL(0), 
                                    D3DVAL(1), 
                                    D3DVAL(0.01))))
    
  8. Create a light frame that is a child of the scene by using the IDirect3DRM3::CreateFrame method. Position the light frame within the scene by using the IDirect3DRMFrame3::SetPosition method. Create a bright point light and add it to the light frame by using the IDirect3DRM3::CreateLightRGB and IDirect3DRMFrame3::AddLight methods.
        if (FAILED(lpD3DRM->CreateFrame(scene, &lights)))
            goto generic_error;
        if (FAILED(lights->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                    -D3DVAL(7)))) 
            goto generic_error;
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(0.9),
                                      D3DVAL(0.8), D3DVAL(0.7), &light1)))
            goto generic_error;
        if (FAILED(lights->AddLight(light1)))
            goto generic_error;
    
  9. Create a dim, ambient light and add it to the scene, again by using the IDirect3DRM3::CreateLightRGB and IDirect3DRMFrame3::AddLight methods.
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1),
                                      D3DVAL(0.1), D3DVAL(0.1), &light2)))
            goto generic_error;
        if (FAILED(scene->AddLight(light2)))
            goto generic_error;
    
  10. Create a bright, parallel point light and add it to the light frame, using the same methods used to create ambient light.
        if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, 
                                      D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9),
                                      &light3)))
            goto generic_error;
        if (FAILED(sun->AddLight(light3)))
            goto generic_error;
    
  11. Clean up by releasing all of the objects created by this function.
        RELEASE(sun_builder);
        RELEASE(planet_builder);
        RELEASE(moon_builder);
        RELEASE(sun_mesh);
        RELEASE(planet_mesh);
        RELEASE(moon_mesh);
        RELEASE(sun);
        RELEASE(planet);
        RELEASE(moon);
        RELEASE(lights);
        RELEASE(light1);
        RELEASE(light2);
        RELEASE(light3);
    

BuildScene Function Source Code

// Create the scene to be rendered. 
 
BOOL BuildScene(LPDIRECT3DRM3 lpD3DRM,  LPDIRECT3DRMDEVICE3 dev, 
                 LPDIRECT3DRMFRAME3 scene, LPDIRECT3DRMFRAME3 camera)
{
    LPDIRECT3DRMMESHBUILDER3 sun_builder = NULL;
    LPDIRECT3DRMMESHBUILDER3 planet_builder = NULL;
    LPDIRECT3DRMMESHBUILDER3 moon_builder = NULL;
    LPDIRECT3DRMMESH sun_mesh = NULL;
    LPDIRECT3DRMMESH planet_mesh = NULL;
    LPDIRECT3DRMMESH moon_mesh = NULL;
    LPDIRECT3DRMFRAME3 sun = NULL;
    LPDIRECT3DRMFRAME3 planet = NULL;
    LPDIRECT3DRMFRAME3 moon = NULL;
    LPDIRECT3DRMLIGHT light1 = NULL;
    LPDIRECT3DRMLIGHT light2 = NULL;
    LPDIRECT3DRMLIGHT light3 = NULL;
    LPDIRECT3DRMFRAME3 lights = NULL;
    HRESULT rval;

    // Create a meshbuilder for each DirectX file to be loaded (one 
    // for sun, planet, and moon) and load the meshes.
    if (FAILED(lpD3DRM->CreateMeshBuilder(&sun_builder)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateMeshBuilder(&planet_builder)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateMeshBuilder(&moon_builder)))
        goto generic_error;

    rval = sun_builder->Load("sphere2.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load sphere2.x file.");
        goto ret_with_error;
    }

    rval = planet_builder->Load("sphere1.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load sphere1.x file.");
    goto ret_with_error;
    }

    rval = moon_builder->Load("sphere0.x", NULL,
                    D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to load sphere0.x file.");
        goto ret_with_error;
    }

    // Adjust the scale (size) of the sun, planet, and moon meshes.
    // Scale equally in the x-, y-, and z-directions to maintain the
    // spherical shape of each mesh. Increase the size of the sun
    // and decrease the size of the planet and moon meshes to
    // achieve the desired relative sizes.
    if (FAILED(sun_builder->Scale(D3DVAL(1.2), D3DVAL(1.2), 
                                  D3DVAL(1.2))))
        goto generic_error;
    if (FAILED(planet_builder->Scale(D3DVAL(0.5), D3DVAL(0.5),
                                     D3DVAL(0.5))))
        goto generic_error;
    if (FAILED(moon_builder->Scale(D3DVAL(0.2), D3DVAL(0.2),
                                   D3DVAL(0.2))))
        goto generic_error;

    // Adjust the color of the sun, planet, and moon meshes.
    // Set the sun to yellow, the planet to greenish-blue, and the
    // moon to red.
    if (FAILED(sun_builder->SetColorRGB(D3DVAL(1.0), D3DVAL(1.0),
                                        D3DVAL(0.0))))
        goto generic_error;
    if (FAILED(planet_builder->SetColorRGB(D3DVAL(0.2), D3DVAL(1.0), 
                                           D3DVAL(0.8))))
        goto generic_error;
    if (FAILED(moon_builder->SetColorRGB(D3DVAL(0.7), D3DVAL(0.2), 
                                         D3DVAL(0.3))))
        goto generic_error;

    // Create hierarchies of frames:
    // - Sun frame within the master scene frame
    // - Planet frame within the sun frame
    // - Moon frame within the planet frame
    if (FAILED(lpD3DRM->CreateFrame(scene, &sun)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateFrame(sun, &planet)))
        goto generic_error;
    if (FAILED(lpD3DRM->CreateFrame(planet, &moon)))
        goto generic_error;

    // Create meshes from the meshbuilders to avoid the same future 
    // conversion by Direct3D Retained Mode. The meshbuilders could be
    // released now, but for clarity they are released with the other
    // objects at the end of this function.
    if (FAILED(sun_builder->CreateMesh(&sun_mesh))) 
        goto generic_error;
    if (FAILED(planet_builder->CreateMesh(&planet_mesh))) 
        goto generic_error;
    if (FAILED(moon_builder->CreateMesh(&moon_mesh))) 
        goto generic_error;

    // Add the meshes into the frames.
    if (FAILED(sun->AddVisual((LPDIRECT3DRMVISUAL) sun_mesh)))
        goto generic_error;
    if (FAILED(planet->AddVisual((LPDIRECT3DRMVISUAL) planet_mesh)))
        goto generic_error;
    if (FAILED(moon->AddVisual((LPDIRECT3DRMVISUAL) moon_mesh)))
        goto generic_error;

    // Set up the frame positions.
    if (FAILED(camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), 
                                   -D3DVAL(25))))
        goto generic_error;
    if (FAILED(sun->SetPosition(scene, D3DVAL(0), D3DVAL(0), 
                                D3DVAL(0))))
        goto generic_error;
    if (FAILED(planet->SetPosition(sun, D3DVAL(7), D3DVAL(0), 
                                   D3DVAL(0))))
        goto generic_error;
    if (FAILED(moon->SetPosition(planet, D3DVAL(0), D3DVAL(3), 
                                 D3DVAL(0))))
        goto generic_error;

    // Set up the frame rotations.
    if (FAILED(sun->SetRotation(scene, D3DVAL(0), D3DVAL(0), 
                                D3DVAL(1), 
                                D3DVAL(0.01))))
        goto generic_error;
    if (FAILED(planet->SetRotation(sun, D3DVAL(1), D3DVAL(0), 
                                   D3DVAL(0), 
                                   D3DVAL(0.02))))
        goto generic_error;
    if (FAILED(moon->SetRotation(planet, D3DVAL(0.1), D3DVAL(0.2),
                               D3DVAL(0.7), 
                               D3DVAL(0.03))))
       goto generic_error;

    // Initialize the lights in the scene. 
    // - Create a light frame. Set its position within the scene 
    //   (straight ahead, directly in front of the sun, to illuminate
    //   the sun) and attach a point light to the light frame.
    // - Create a soft, ambient light and add it to the scene.
    // - Create a bright, parallel point light and add it to the sun
    //   frame to properly illuminate the planet and moon as they
    //   revolve around the sun.
     
    // Create a point light in front of sun. 
    if (FAILED(lpD3DRM->CreateFrame(scene, &lights)))
        goto generic_error;
    if (FAILED(lights->SetPosition(scene, D3DVAL(0), D3DVAL(0),
                                -D3DVAL(7)))) 
        goto generic_error;
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(0.9),
                                  D3DVAL(0.8), D3DVAL(0.7), &light1)))
        goto generic_error;
    if (FAILED(lights->AddLight(light1)))
        goto generic_error;

    // Create an ambient light and add it to the scene. 
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1),
                                  D3DVAL(0.1), D3DVAL(0.1), &light2)))
        goto generic_error;
    if (FAILED(scene->AddLight(light2)))
        goto generic_error;

    // Create a parallel point light and add it to the sun frame. 
    if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, 
                                  D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9),
                                  &light3)))
        goto generic_error;
    if (FAILED(sun->AddLight(light3)))
        goto generic_error;

    // Clean up.
    RELEASE(sun_builder);
    RELEASE(planet_builder);
    RELEASE(moon_builder);
    RELEASE(sun_mesh);
    RELEASE(planet_mesh);
    RELEASE(moon_mesh);
    RELEASE(sun);
    RELEASE(planet);
    RELEASE(moon);
    RELEASE(lights);
    RELEASE(light1);
    RELEASE(light2);
    RELEASE(light3);
    return TRUE;

generic_error:
    DISPLAYMSG("A failure occurred while building the scene.");
ret_with_error:
    RELEASE(sun_builder);
    RELEASE(planet_builder);
    RELEASE(moon_builder);
    RELEASE(sun_mesh);
    RELEASE(planet_mesh);
    RELEASE(moon_mesh);
    RELEASE(sun);
    RELEASE(planet);
    RELEASE(moon);
    RELEASE(lights);
    RELEASE(light1);
    RELEASE(light2);
    RELEASE(light3);
    return FALSE;
}

RenderScene

RenderScene performs the actual drawing to the screen. It draws the scene that BuildScene created by performing the following steps.

  1. Move the scene by using IDirect3DRMFrame3::Move.
  2. Clear the viewport by using IDirect3DRMViewport2::Clear.
  3. Render the scene by using IDirect3DRMViewport2::Render.
  4. Update the window by using IDirect3DRMDevice3::Update.

RenderScene Function Source Code

// Clear the viewport, render the next frame, and update the window.
 
static BOOL RenderScene()
{
    HRESULT rval;

    // Move the scene.
    rval = myglobs.scene->Move(D3DVAL(1.0));
    if (FAILED(rval)) 
    {
        DISPLAYMSG("Moving scene failed.");
        return FALSE;
    }

    // Clear the viewport.
    rval = myglobs.view->Clear();
    if (FAILED(rval)) 
    {
        DISPLAYMSG("Clearing viewport failed.");
        return FALSE;
    }

    // Render the scene to the viewport.
    rval = myglobs.view->Render(myglobs.scene);
    if (FAILED(rval)) 
    {
        DISPLAYMSG("Rendering scene failed.");
        return FALSE;
    }

    // Update the window.
    rval = myglobs.dev->Update();
    if (FAILED(rval)) 
    {
        DISPLAYMSG("Updating device failed.");
        return FALSE;
    }
    return TRUE;
}

AddMediaPath

AddMediaPath provides a path to the textures and .x files needed by the application. The function searches the system registry for the location of the DirectX 6.x samples, prepends that location to the path of the directory containing the Retained Mode media files, and adds the whole path to the media files to the end of the current file search path.

AddMediaPath Function Source Code

// Looks in the system registry to determine the media path for the
// sample, then adds that path to the end of the current file search path. 

VOID AddMediaPath(LPDIRECT3DRM3 pD3DRM)
{
    HKEY   key;
    LONG   result;
    TCHAR  strPath[512];
    DWORD  type, size = 512;

    // Open the registry
    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectX",
                          0, KEY_READ, &key);
    if(ERROR_SUCCESS != result)
        return;
		
    // Search for the desired registry value, and close the registry
    result = RegQueryValueEx(key, "DX6SDK Samples Path", NULL, &type, 
                             (BYTE*)strPath, &size);
    RegCloseKey(key);

    if(ERROR_SUCCESS != result)
        return;

    strcat(strPath, "\\D3DRM\\Media");

    pD3DRM->AddSearchPath(strPath);

	return;
}

CreateObjects

CreateObjects initializes the sample's global variables and creates objects. After initializing global variables, CreateObjects performs the steps necessary to create a device, associate it with the application's main window, and create the master scene frame, the camera frame, and the viewport as follows:

  1. Use the DirectDraw DirectDrawCreateClipper function to create a DirectDrawClipper object.
  2. Use the DirectDrawClipper object's SetHWnd method to associate the application's window with the DirectDrawClipper. The DirectDrawClipper object restricts subsequent drawing to a designated area—in this case, the application window.
  3. Use the Direct3DRMCreate function to create a Direct3D Retained Mode object, then query for the IDirect3DRM3 interface.
  4. Use the Windows GetClientRect function to obtain the height and width of the application's main window.
  5. Use the Direct3D Retained Mode object's CreateDeviceFromClipper method to create a device, passing NULL to create a default device, and passing the width and height of the application's window to the method.
  6. Create a Direct3D Retained Mode window device by querying the device for support of the IDirect3DRMWinDevice interface. The window device handles the WM_ACTIVATE and WM_PAINT messages.
  7. Create the master scene frame by using IDirect3DRM3::CreateFrame.
  8. Create the camera frame by using IDirect3DRM3::CreateFrame.
  9. Create the Direct3D Retained Mode viewport by using IDirect3DRM3::CreateViewport.
  10. Use BuildScene to create the scene.

CreateObjects Function Source Code

// Initialize globals, and create the device and objects. 
 
BOOL CreateObjects(HWND win)
{
    HRESULT rval; // Return value 
    RECT rc;      // Bounding rectangle for main window 
    int width;    // Device's width 
    int height;   // Device's height 

    // Initialize the entire global variable structure to zero. 
    memset(&myglobs, 0, sizeof(myglobs));

    // Create a DirectDrawClipper object and associate the window with it.
    rval = DirectDrawCreateClipper(0, &lpDDClipper, NULL);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create DirectDrawClipper object.");
        return FALSE;
    }
    rval = lpDDClipper->SetHWnd(0, win);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to set the window handle for the DirectDrawClipper.");
        return FALSE;
    }

   // Create the Direct3DRM object.
    LPDIRECT3DRM pD3DRMTemp;
    rval = Direct3DRMCreate(&pD3DRMTemp);
    if (FAILED(rval)) 
    {
        DISPLAYMSG("Failed to create Direct3DRM.");
        return FALSE;
    }
    if( FAILED(pD3DRMTemp->QueryInterface(IID_IDirect3DRM3, (void **)&lpD3DRM)))
    {
        RELEASE(pD3DRMTemp);
        DISPLAYMSG("Failed query for Direct3DRM3.\n" );
        return FALSE;
    }
    RELEASE(pD3DRMTemp);    
	
    // Create a default Direct3D Retained Mode device.
    GetClientRect(win, &rc);

    rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper, 
                           NULL, // Default device 
                           rc.right, rc.bottom, &myglobs.dev);

    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the D3DRM device.");
        return FALSE;
    }


    // Create a Direct3D Retained Mode window device to handle the
    // WM_ACTIVATE and WM_PAINT messages.
    rval = myglobs.dev->QueryInterface(IID_IDirect3DRMWinDevice,
                                       (void **) &myglobs.windevice);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the window device.");
        return FALSE;
    }
    
    
    // Create the master scene frame and the camera frame.
    rval = lpD3DRM->CreateFrame(NULL, &myglobs.scene);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the master scene frame.");
        return FALSE;
    }

    rval = lpD3DRM->CreateFrame(myglobs.scene, &myglobs.camera);
    if (FAILED(rval)) {
        DISPLAYMSG("Failed to create the camera's frame.");
        return FALSE;
    }

    // Create the Direct3D Retained Mode viewport using the device, camera frame,
    // and the device's width and height.
    width = myglobs.dev->GetWidth();
    height = myglobs.dev->GetHeight();

    rval = lpD3DRM->CreateViewport(myglobs.dev, myglobs.camera, 0, 0, 
                                   width, height, &myglobs.view);
    if (FAILED(rval)) {
        myglobs.bInitialized = FALSE;
        RELEASE(myglobs.dev);
        return FALSE;
    }

	// Add the media path so textures and .x files can be found.
	AddMediaPath( lpD3DRM );

    // Create the scene to be rendered.
    if (!BuildScene(lpD3DRM, myglobs.dev, myglobs.scene, 
                    myglobs.camera))
        return FALSE;

    // Globals are initialized.
    myglobs.bInitialized = TRUE;

    return TRUE;
}

InitApp

The InitApp function creates the main window class and the main window, as is typical of Windows applications. The only Direct3D Retained Mode–related call in InitApp is the call to CreateObjects.

InitApp Function Source Code

// Create the main window and initialize objects. 
 
static HWND InitApp(HINSTANCE this_inst, int cmdshow)
{
    HWND win;     // Main window handle 
    WNDCLASS wc;

    // Set up and register the window class.
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = sizeof(DWORD);
    wc.hInstance = this_inst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "D3DRM Example";
    if (!RegisterClass(&wc))
        return FALSE;

    // Create the window.
    win = CreateWindow(   
            "D3DRM Example",                   // class 
            "RMBegin2: Direct3DRM Sample Two", // caption 
            WS_OVERLAPPEDWINDOW,               // style 
            CW_USEDEFAULT,                     // init. x pos 
            CW_USEDEFAULT,                     // init. y pos 
            350,                               // init. x size 
            300,                               // init. y size 
            NULL,                              // parent window 
            NULL,                              // menu handle 
            this_inst,                         // program handle 
            NULL                               // create parms 
        );
    if (!win)
        return FALSE;

    // Initialize global variables, enumerate devices, and create the 
    // Direct3D Retained Mode objects.
    if (!CreateObjects(win))
        return FALSE;

    // Display the window.
    ShowWindow(win, cmdshow);
    UpdateWindow(win);

    return win;
}

WinMain

WinMain provides the main message loop for the application. As long as the global variables are initialized and the application is not minimized, WinMain uses RenderScene to draw the scene to the screen.

WinMain Function Source Code

// Initialize the application, process messages, and render the scene.
 
int APIENTRY WinMain (HINSTANCE this_inst, 
                      HINSTANCE prev_inst, 
                      LPSTR cmdline, 
                      int cmdshow)
{
    HWND    hwnd;
    MSG     msg;
    prev_inst;
    cmdline;

    // Create the window and initialize objects. 
    if (!(hwnd = InitApp(this_inst, cmdshow)))
        return 1;

    while (TRUE) {  
        // Monitor the message queue and process messages. PeekMessage 
        // returns control to the application immediately so that the 
        // application can both process messages and continue rendering. 
        // If the message is WM_QUIT, break out of this function
        // because the application is terminating.
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {

            if (msg.message == WM_QUIT) 
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        // If the application is not minimized, and the Direct3D Retained 
        // Mode objects are initialized, render.
        if (!myglobs.bMinimized && myglobs.bInitialized) {

            // Render one frame of the scene. If rendering fails, post
            // the WM_DESTROY message, enabling the application to 
            // free resources before terminating the application.
            if (!RenderScene()) {
                DISPLAYMSG("Rendering failed. Aborting execution.");
                PostMessage(NULL, WM_DESTROY, 0, 0);
                break;
            }

        // Yield to other applications if this application is
        // not currently rendering.
        } else 
              WaitMessage();

    }
    return msg.wParam;
}

WindowProc

WindowProc handles messages for the main application window. WindowProc sets a flag so that the application stops rendering while minimized. This function also handles resizing of the application window, activating the window, painting, and releasing objects when the application shuts down.

When the user resizes the application window, the RMBegin2 sample increases or decreases the size of the scene so that it continues to fill the window. This function performs the following steps to handle the WM_SIZE message (window resizing).

WindowProc uses the window device to handle the WM_ACTIVATE and WM_PAINT messages by using the IDirect3DRMWinDevice::HandleActivate and IDirect3DRMWinDevice::HandlePaint methods, respectively.

WindowProc Function Source Code

// Handle messages for the main window.
 
LRESULT CALLBACK WindowProc(HWND win, 
                            UINT msg, 
                            WPARAM wparam, 
                            LPARAM lparam)
{
HRESULT rval;

switch (msg) {

    case WM_SIZE:
        // Handle resizing of the window. If the application was 
        // minimized, set a flag so that rendering will stop while minimized
        // and break out of this function. Otherwise, make sure the
        // minimized flag is clear and check for window resizing other
        // than minimized.
        {
        if (SIZE_MINIMIZED == wparam) 
        {
            myglobs.bMinimized = TRUE;
            break;
        }
        else
            myglobs.bMinimized = FALSE;

        // Handle resizing of the window. Obtain the window's current
        // width and height.
        int width = LOWORD(lparam);
        int height = HIWORD(lparam);

        // If the global variables, such as the viewport and device, 
        // have been initialized, obtain the viewport's and the
        // device's width and height. Create a new device and window
        // device or viewport, as needed. A new device and window device
        // are only needed if the window is larger than the old device.
        if (myglobs.bInitialized) 
        {
            int view_width = myglobs.view->GetWidth();
            int view_height = myglobs.view->GetHeight();
            int dev_width = myglobs.dev->GetWidth();
            int dev_height = myglobs.dev->GetHeight();

            // Check whether the window size increased in either
            // width or height.
            if (width > dev_width || height > dev_height)
            {
                // Application window is now larger than the current  
                // device. Destroy the old viewport and device and create 
                // new ones. Be sure to free the viewport before freeing
                // the device, or a general protection fault might occur.
                myglobs.bInitialized = FALSE;
                RELEASE(myglobs.view);
                RELEASE(myglobs.dev);

                // Create a new, default Direct3D Retained Mode device.
                rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper, 
                                        NULL, // Default device 
                                        width, height, 
                                        &myglobs.dev);

                if (FAILED(rval)) 
                {
                    // If the new device could not be created, notify 
                    // the user and shut down the application.
                    DISPLAYMSG("Failed to create the D3DRM device.");
                    PostMessage(NULL, WM_DESTROY, 0, 0);
                    return FALSE;
                } 

                // Destroy the old Direct3D Retained Mode window device 
                // and create a new one to match the newly created 
                // device. The window device handles the WM_ACTIVATE and 
                // WM_PAINT messages.
                RELEASE(myglobs.windevice);
                rval = myglobs.dev->QueryInterface(
                                       IID_IDirect3DRMWinDevice,
                                       (void **) &myglobs.windevice);
                if (FAILED(rval)) {
                    DISPLAYMSG("Failed to create the window device.");
                    return FALSE;
                }

            }

            // If the window size changed, free the old viewport and
            // create a new viewport of the proper size.
            myglobs.bInitialized = FALSE;
            RELEASE(myglobs.view);
            rval = lpD3DRM->CreateViewport(myglobs.dev, 
                                           myglobs.camera,
                                           0, 0, width, height,
                                           &myglobs.view);
            if (FAILED(rval)) 
            {
                // If the new viewport could not be created, notify 
                // the user and shut down the application.
                DISPLAYMSG("Failed to create a new viewport.");
                PostMessage(NULL, WM_DESTROY, 0, 0);
                break;
            }

            myglobs.bInitialized = TRUE;
        }
        }
        break;

    case WM_ACTIVATE:
        // Use the window device's HandleActivate method to handle 
        // window activation.
        if (myglobs.bInitialized)
            if (FAILED(myglobs.windevice->HandleActivate(wparam)))
                DISPLAYMSG("Failed to handle WM_ACTIVATE.");
        break;

    case WM_PAINT:
        // Use the window device's HandlePaint method to handle 
        // repainting, unless global variables have not yet been 
        // initialized. 
        if (!myglobs.bInitialized)
            return DefWindowProc(win, msg, wparam, lparam);

        // Use the window device to handle repainting.
        RECT rc;
        PAINTSTRUCT ps;

        if (GetUpdateRect(win, &rc, FALSE)) {
            BeginPaint(win, &ps);
            if (FAILED(myglobs.windevice->HandlePaint(ps.hdc)))
                DISPLAYMSG("Failed to handle WM_PAINT.");
        } 
        EndPaint(win, &ps);
        break;

    case WM_DESTROY:
        // Clear the bInitialized flag, free objects, and post the WM_QUIT
        // message to terminate the application. Be sure to free the 
        // viewport before freeing the device, or a general protection
        // fault might occur.
        myglobs.bInitialized = FALSE;
        RELEASE(myglobs.view);
        RELEASE(myglobs.windevice);
        RELEASE(myglobs.dev);
        RELEASE(lpD3DRM);
        RELEASE(lpDDClipper);
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(win, msg, wparam, lparam);
    }
    return 0L;
}

Related Topics

Now that you are familiar with the basics of creating objects and putting something on the screen using Direct3D Retained Mode, the code in the Direct3D Retained Mode samples should be easier to understand. Nearly all of these samples use the helper code provided by RMMain to create a device and render a scene. They each provide their own BuildScene function to create unique scenes.

As a next step, you can look at some of the other Direct3D Retained Mode samples, such as Egg or Globe, which provide functionality similar to RMBegin1 and RMBegin2.

For information about another common task in Direct3D Retained Mode, see Enumerating Devices: The RMEnum Sample, which provides a walkthrough of the RMEnum sample code. RMEnum focuses on how to enumerate devices.

For brief summaries of all of the Retained Mode samples, see Samples.


Top of Page Top of Page
© 2000 Microsoft and/or its suppliers. All rights reserved. Terms of Use.