Creating a device, building a scene, and moving the scene are fundamental concepts in Microsoft® Direct3D® Retained Mode. The RMBegin1 sample demonstrates these concepts by creating a default device, loading a mesh from a Microsoft DirectX® file, adding color to the object and lights to the scene, and rotating the object. In the process of building the scene, the sample uses a meshbuilder, frames, lights, and a material. The sample also creates a DirectDrawClipper object, viewport and camera.
This article walks you through code from the RMBegin1 sample. The sample source code consists of one self-contained file (RMBegin1.cpp). It does not use any separate header files, except for the standard Microsoft Windows®, DirectDraw®, and Direct3D Retained Mode header files for the sake of simplicity. It also does not contain any menus, icons, or other resources.
This sample uses the Teapot.x file from the DirectX Foundation Software Development Kit (SDK) to provide the teapot image 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. Teapot.x resides in that directory, allowing the application to locate the file automatically. If you have not installed the SDK and simply run RMBegin1.exe, Teapot.x must be located in the same directory as RMBegin1.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.
#define INITGUID
#include <windows.h> // Standard Windows header file #include <direct.h> // DirectDraw definitions #include <d3drmwin.h> // Direct3D Retained Mode definitions
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, viewport, scene, and camera information, as well as information about whether the application is minimized and whether objects have been initialized.
// Global Variables LPDIRECT3DRM3 lpD3DRM = NULL; // Direct3DRM object LPDIRECTDRAWCLIPPER lpDDClipper = NULL; // DirectDrawClipper object // Global Structure struct _myglobs { // Direct3DRM device. LPDIRECT3DRMDEVICE3 dev; // 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;
RMBegin1.cpp contains the following functions.
BuildScene creates the scene to be rendered. It loads a mesh from the DirectX file, adds color to the mesh, 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.
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.
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, camera frame, scene frame, and viewport. CreateObjects uses BuildScene to create the scene to be rendered. InitApp uses CreateObjects.
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.
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.
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
WindowProc handles messages for the application. It handles cases where the application is minimized or destroyed.
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
For more information about these functions, see the following topics in this article.
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 section of this article provides code fragments from the BuildScene function and presents the steps the RMBegin1 sample performs to build the scene. This section also presents the complete source code for the RMBegin1 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.
if (FAILED(lpD3DRM->CreateMeshBuilder(&meshbuilder))) goto generic_error; rval = meshbuilder->Load("teapot.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL);
if (FAILED(lpD3DRM->CreateFrame(scene, &childframe))) goto generic_error;
if (FAILED(childframe->AddVisual((LPDIRECT3DRMVISUAL)meshbuilder))) goto generic_error;
rval = camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), -D3DVAL(7));
if (FAILED(childframe->SetRotation(scene, D3DVAL(0), D3DVAL(1), D3DVAL(0), D3DVAL(0.03)))) // angle goto generic_error;
if (FAILED(lpD3DRM->CreateFrame(scene, &lights))) goto generic_error;
if (FAILED(lights->SetPosition(scene, D3DVAL(5), D3DVAL(0), -D3DVAL(7)))) goto generic_error;
if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(0.8), D3DVAL(0.9), &light1))) goto generic_error; if (FAILED(lights->AddLight(light1))) goto generic_error;
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;
if (FAILED(lpD3DRM->CreateMaterial(D3DVAL(10.0), &mat))) goto generic_error; if (FAILED(meshbuilder->SetMaterial(mat))) goto generic_error;
if (FAILED(meshbuilder->SetColorRGB(D3DVAL(0.0), // red D3DVAL(0.7), // green D3DVAL(0.0)))) // blue goto generic_error;
RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat);
// Create the scene to be rendered. BOOL BuildScene( LPDIRECT3DRM3 lpD3DRM, LPDIRECT3DRMDEVICE3 dev, LPDIRECT3DRMFRAME3 scene, LPDIRECT3DRMFRAME3 camera ) { LPDIRECT3DRMFRAME3 lights = NULL; LPDIRECT3DRMMESHBUILDER3 meshbuilder = NULL; LPDIRECT3DRMFRAME3 childframe = NULL; LPDIRECT3DRMLIGHT light1 = NULL; LPDIRECT3DRMLIGHT light2 = NULL; LPDIRECT3DRMMATERIAL2 mat = NULL; HRESULT rval; // Load a mesh from a DirectX file. if (FAILED(lpD3DRM->CreateMeshBuilder(&meshbuilder))) goto generic_error; rval = meshbuilder->Load("teapot.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL); if (FAILED(rval)) { DISPLAYMSG("Failed to load .x file."); goto ret_with_error; } // Create a child frame within the scene. if (FAILED(lpD3DRM->CreateFrame(scene, &childframe))) goto generic_error; // Add the loaded mesh into the child frame. if (FAILED(childframe->AddVisual((LPDIRECT3DRMVISUAL)meshbuilder))) goto generic_error; // Set up the camera frame's position. Objects with the same x-value and // y-value as the camera will appear straight ahead. // Negative z-values are farther away, making the object look // smaller as the negative numbers increase. rval = camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), -D3DVAL(7)); if (FAILED(rval)) { DISPLAYMSG("Failed to position the camera in the frame."); goto ret_with_error; } // Rotate the child mesh around the y-axis (zero x-value and z-value) // and use a small rotational angle to rotate slowly. if (FAILED(childframe->SetRotation(scene, D3DVAL(0), D3DVAL(1), D3DVAL(0), D3DVAL(0.03)))) // angle goto generic_error; // Initialize the lights in the scene, creating a light frame that // is a child of the scene. if (FAILED(lpD3DRM->CreateFrame(scene, &lights))) goto generic_error; // Position the light frame within the scene. This light comes from // the right of the camera. It has a different x-value, but the same // y-value and z-value as the camera position. if (FAILED(lights->SetPosition(scene, D3DVAL(5), D3DVAL(0), -D3DVAL(7)))) goto generic_error; // Create a bright, parallel point light and add it to the light frame. // Color values must be in the range 0.0 (dim) to 1.0 (bright). if (FAILED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(0.8), D3DVAL(0.9), &light1))) goto generic_error; if (FAILED(lights->AddLight(light1))) goto generic_error; // Create a dim, ambient light and add it to the scene frame, // applying it to the whole scene. Ambient light comes from all // directions, so a bright ambient light would wash out the object. 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 material, setting the reflectiveness (5.0 is metallic, // higher is more plastic) on the previously loaded mesh. if (FAILED(lpD3DRM->CreateMaterial(D3DVAL(10.0), &mat))) goto generic_error; if (FAILED(meshbuilder->SetMaterial(mat))) goto generic_error; // Set the mesh color (bright green in this case). if (FAILED(meshbuilder->SetColorRGB(D3DVAL(0.0), // red D3DVAL(0.7), // green D3DVAL(0.0)))) // blue goto generic_error; // Clean up. RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat); return TRUE; generic_error: DISPLAYMSG("A failure occured while building the scene."); ret_with_error: RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat); return FALSE; }
RenderScene performs the actual drawing to the screen. It draws the scene that BuildScene created by performing the following steps.
// 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 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 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.
// 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 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:
// Initialize globals, 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 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; }
The InitApp function creates the main window class and the main window, as is typical of Windows applications. The only Direct3D Retained Moderelated call in InitApp is the call to CreateObjects.
// Create the main window and initialize objects. static HWND InitApp(HINSTANCE this_inst, int cmdshow) { HWND win; // Main window handle WNDCLASS wc; // Window class // 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 "RMBegin1: Direct3DRM Sample One", // 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 and create the Direct3D Retained // Mode objects. if (!CreateObjects(win)) return FALSE; // Display the window. ShowWindow(win, cmdshow); UpdateWindow(win); return win; }
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.
// 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 handles messages for the main application window. WindowProc sets a flag so that the application stops rendering while minimized. This function also releases objects when the application shuts down.
// Handle messages for the main window. LRESULT CALLBACK WindowProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_SIZE: // Handle minimizing of the window. If the application was // minimized, set a flag so that rendering will stop while // minimized. Otherwise, continue rendering. if (SIZE_MINIMIZED == wparam) myglobs.bMinimized = TRUE; else myglobs.bMinimized = FALSE; 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.dev); RELEASE(lpD3DRM); RELEASE(lpDDClipper); PostQuitMessage(0); break; default: return DefWindowProc(win, msg, wparam, lparam); } return 0L; }
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 the RMBegin2 sample, which extends the functionality provided by RMBegin1. You could also look at some of the Direct3D Retained Mode samples, such as Egg or Globe, that provide functionality similar to RMBegin1.
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
© 2000 Microsoft and/or its suppliers. All rights reserved. Terms of Use.