Rotating an Object: The RMBegin1 Sample
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.
Dependencies
- RMBegin1 links to DDraw.lib and D3DRM.lib.
- RMBegin1 loads the teapot image from Teapot.x, as described in the previous section.
- To properly initialize globally unique identifiers (GUIDs), Direct3D applications must contain the following definition at the top of their code.
#define INITGUID- The sample's RMBegin1.cpp file includes the following header files.
#include <windows.h> // Standard Windows header file #include <direct.h> // DirectDraw definitions #include <d3drmwin.h> // Direct3D Retained Mode definitionsGlobal 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, 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;Function Summaries
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.
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 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.
- Load a mesh from a DirectX file by using the IDirect3DRM3::CreateMeshBuilder and IDirect3DRMMeshBuilder3::Load methods.
if (FAILED(lpD3DRM->CreateMeshBuilder(&meshbuilder))) goto generic_error; rval = meshbuilder->Load("teapot.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL);- Create a child frame within the scene by using the IDirect3DRM3::CreateFrame method.
if (FAILED(lpD3DRM->CreateFrame(scene, &childframe))) goto generic_error;- Add the loaded mesh into the child frame by using the IDirect3DRMFrame3::AddVisual method.
if (FAILED(childframe->AddVisual((LPDIRECT3DRMVISUAL)meshbuilder))) goto generic_error;- Set the camera position within the scene by using the IDirect3DRMFrame3::SetPosition method.
rval = camera->SetPosition(scene, D3DVAL(0), D3DVAL(0), -D3DVAL(7));- Set the child frame's rotation by using the IDirect3DRMFrame3::SetRotation method.
if (FAILED(childframe->SetRotation(scene, D3DVAL(0), D3DVAL(1), D3DVAL(0), D3DVAL(0.03)))) // angle goto generic_error;- Create a light frame that is a child of the scene by using the IDirect3DRM3::CreateFrame method.
if (FAILED(lpD3DRM->CreateFrame(scene, &lights))) goto generic_error;- Position the light frame within the scene by using the IDirect3DRMFrame3::SetPosition method.
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 by using the IDirect3DRM3::CreateLightRGB and IDirect3DRMFrame3::AddLight methods.
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, again by using 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;- Create a material and set the reflectiveness by using the IDirect3DRM3::CreateMaterial and IDirect3DRMMeshBuilder3::SetMaterial methods.
if (FAILED(lpD3DRM->CreateMaterial(D3DVAL(10.0), &mat))) goto generic_error; if (FAILED(meshbuilder->SetMaterial(mat))) goto generic_error;- Set the mesh color by using the IDirect3DRMMeshBuilder3::SetColorRGB method.
if (FAILED(meshbuilder->SetColorRGB(D3DVAL(0.0), // red D3DVAL(0.7), // green D3DVAL(0.0)))) // blue goto generic_error;- Clean up by releasing all of the objects created by this function.
RELEASE(childframe); RELEASE(lights); RELEASE(meshbuilder); RELEASE(light1); RELEASE(light2); RELEASE(mat);BuildScene Function Source Code
// 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
RenderScene performs the actual drawing to the screen. It draws the scene that BuildScene created by performing the following steps.
- Move the scene by using IDirect3DRMFrame3::Move.
- Clear the viewport by using IDirect3DRMViewport2::Clear.
- Render the scene by using IDirect3DRMViewport2::Render.
- 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 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:
- Use the DirectDraw DirectDrawCreateClipper function to create a DirectDrawClipper object.
- Use the DirectDrawClipper's SetHWnd method to associate the application's window with the DirectDrawClipper. The DirectDrawClipper object restricts subsequent drawing to a designated areain this case, the application window.
- Use the Direct3DRMCreate function to create a Direct3D Retained Mode object, then query for the IDirect3DRM3 interface.
- Use the Windows GetClientRect function to obtain the height and width of the application's main window.
- 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.
- Create the master scene frame by using IDirect3DRM3::CreateFrame.
- Create the camera frame by using IDirect3DRM3::CreateFrame.
- Create the Direct3D Retained Mode viewport by using IDirect3DRM3::CreateViewport.
- Use BuildScene to create the scene.
CreateObjects Function Source Code
// 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; }InitApp
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.
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; // 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
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 releases objects when the application shuts down.
WindowProc Function Source Code
// 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; }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 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
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.