Direct3D Retained Mode
Bipin Patwardhan
Abstract
The DirectX technology developed by Microsoft, is a collection
of APIs for developing high-performance, real-time Windows
applications.
These APIs have been developed primarily to attract all the
developers who have uptil now developed games and multimedia
applications on MSDOS, as developing these applications on Windows
makes them slow. A primary motivation behind the DirectX APIs is to
allow Windows applications to rival the performace of MSDOS
applications. In addition to improvement in performance, these APIs
offer many other features, like sound handling, networking.
DirectX consists of DirectDraw, DirectSound, DirectPlay,
Direct3D and DirectInput. Each one of these components
addresses a different area required for game and multimedia
application development.
In this series of tutorials, we will focus on the Direct3D
component of DirectX.
Direct3D offers services for real-time 3D, to deliver fast software
based rendering of the full 3D rendering pipeline. It also provides
transparent access to underlying hardware acceleration, if present.
This tutorial will present the details of the retained mode of
Direct3D.
MotivationIn a previous tutorial, we have given an overview of the Direct3D API. We also briefly covered the different components of DirectX, the file format used to define 3D objects in Direct3D and also how to use COM objects through the C++ and C programming languages.
In this tutorial, we will cover one of the two modes of Direct3D. The mode we will be covering in this tutorial, is the retained mode. We will cover the other mode, namely the immediate mode in another tutorial.
For the retained mode, we will first give an overview of the retained mode. Then we will cover the steps to be followed to create a retained mode application, followed by the scene description of the 3D scene to be displayed. To concretise the steps of application creation, we will cover a sample application, which will also help in clarifying the different objects, methods and functions used while creating retained mode applications. to wrap up the tutorial, we will cover some of the objects that are provided by the retained mode, to get an idea of the things possible using the retained mode.
IntroductionAs we mentioned in the overview of Direct3D ([7]), the retained mode is one of the two modes in which we can operate Direct3D. The other mode is the immediate mode, which will be covered in [6].
The retained mode is the higher-level mode of the two modes of Direct3D and provides a higher-level API to add 3D content to applications.
The retained mode is designed for manipulating 3D objects and 3D scenes. This is done by providing functions for creating and managing the scene and for manipulating the 3D objects to be displayed as a part of the scene.
A feature of the retained mode that is significant is that it uses the immediate mode, transparent to the application developer. The retained mode is built on top of the immediate mode and uses all the features of the immediate mode, to do its work, without exposing the low-level details to the application developer who is develping an application using the retained mode. The retained mode is also tightly coupled with DirectDraw, the API for fast 2D bitmap operations.
FeaturesA very important feature of the retained mode is that it provides a built-in geometry engine. This geometry engine manages the rendering pipeline and hence relieves the application of the mathematical calculations and equations, making the application simpler to develop. The engine supports features like texture mapping, light calculations, managing material properties, managing the ambient lighting conditions of the scene, the specular lighting of each object, the different shading models, like flat shading, gouraud shading and phong shading. The phong shading model is not supported in the present version of Direct3D. Retained mode also supports object display using points and as a wireframe. The engine also supports fixed path and keyframe animation. For more details on computer graphics, refer [3], [4], [9], [10], [11] and [12].
SupportThe retained mode is a higher-level interface to the 3D functions provided by Direct3D and it provides support for 3D scene management and rendering. It provides very good support by maintaining the objects that are part of the scene, in an object database. It also manages the internal structures of the objects and manages the objects as they are manipulated in the scene, using the different transformations. Though scene management and object database management is a non-trivial task, the interface provided to the developer is simple and using this interface to manipulate the objects is quite easy.
Steps to Create an ApplicationAfter the introduction to the retained mode of Direct3D and its various features, let us now consider the steps to be followed to create an application using the retained mode.
To create a retained mode application, we need to create the Direct3D objects that are part of the scene and add them to the scene that is to be displayed. We then need to set the rendering state of the engine so that we can display the objects as desired. We then render the scene to display the scene.
The steps to create a retained mode application, are given below:
SceneAfter covering the steps to create a retained mode application and before considering a sample application, let us cover how a scene, to be rendered, has to be specified to the retained mode.
DescriptionA scene in retained mode is a hierarchy of frames, with the retained mode providing the basic frame object required to create this heirarchy. All frames, except the master frame have one and only one parent. A frame may or may not have child frame, below it. All frames have a position and an orientation. Only the master frame does not have a parent frame. The position and orientation of a frame is relative to the position and orientation of its parent frame. This relative positioning and orientation helps model the scene using hierarchic modeling, in which we can apply a transformation on one piece and it gets reflected on all the children as well. For a discussion on hierarchic modeling, refer [3].
As we mentioned, a scene definition consists of a hierarchy of frames. The master frame is the topmost parent, which does not have a parent. The camera to be used to set the viewing parameters, is also a frame and is attached to the master frame. We usually definr another frame, the world frame, below the master frame, for placing the objects and lights in the scene. This is done for easy manipulation of all the objects and lights in the scene, without affecting the camera and other such parameters. To add objects and lights to the scene, they are either added to their own frame, which in turn is added to the world frame. All objects and lights are placed in the world frame, directly or indirectly. Ambient light if any, is added to the master frame directly.
CreationTo create a scene, the following steps need to be followed:
If required, objects and lights can have their own frames and these in turn are added to the scene, using the frames. Defining a frame for one single object or light gives us the advantage of manipulating only one object at a time, using its frame, without affecting the other objects in the scene.
Light CreationTo create a light with its own frame, the following steps have to be followed:
Object LoadingTo create an object with its own frame, the following steps have to be followed:
Sample SceneConsider the following scene description.
The scene consists of three objects, namely two spheres and one teapot object. It also has a directional light source, which uses the ramp model. The light is from the bottom right corner of the scene.
The scene, as it will appear when rendered, is shown in the figure 1.
Figure 1: Sample Scene
A diagrammatic representation of the frame hierarchy of the described scene is shown in figure 2.
Figure 2: Sample Scene Hierarchy
In figure 2, the ambient light source is shown with dashed lines, to indicate that it is not present in the current scene. It is there, just to indicate the position of the ambient light, if added to the scene.
Sample ApplicationLet us now consider a sample application and go through its source code, to understand the steps required to create an application.
In this application, we will be going through the standard procedure to create a Direct3D retained mode application. The scene to be rendered, consists of a sphere and a light source. The sphere is loaded from a file (``sphere4.x'') and has a texture map (``hello.bmp'') associated with it. To do some work when the application is idle, we will rotate the sphere around its y axis.
The sphere, as it will look if displayed as a wireframe, is shown in figure 3.
Figure 3: Wireframe Sphere
The image used for texture mapping is shown in figure 4.
Figure 4: Texture Image
One position in the rotation of the sphere, during execution, are shown in figure 5.
Figure 5: Position Sample Application Source CodeWe will now cover the source code for each of the steps described in a previous section.
Step 1: Define Global VariablesThe source code for this step is shown in figure 6.
#define INITGUID LPDIRECT3DRM lpD3DRM; LPDIRECTDRAWCLIPPER lpDDClipper; struct taInf { LPDIRECT3DRMDEVICE dev; LPDIRECT3DRMVIEWPORT view; LPDIRECT3DRMFRAME scene, camera; GUID DriverGUID[MAX_DRIVERS]; char DriverName[MAX_DRIVERS][50]; int NumDrivers, CurrDriver; BOOL bQuit, bInitialized, bMinimized; int BPP; } aInf;
In this step, we have put all the required variables into one structure, for convenience, during initialization. Some of the variables used are:
Step 2: Setup WindowsThe source code for this step is shown in figure 7.
memset(&aInf, 0, sizeof(aInf)); RegisterClass(&wc) win = CreateWindow("Hello", "Hello World -- D3DRM Style", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);
In this step, we create the Window on which the scene is to be rendered. (For more details on creation of Windows using SDK, refer [8].)
Step 3: Setup Direct3DThe source code for this step is shown in figure 8.
// get bits per pixel /depth hdc = GetDC(win); aInf.BPP = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(win, hdc); // enumerate device drivers EnumDrivers(win); // create Direct3D object lpD3DRM = NULL; Direct3DRMCreate(&lpD3DRM);
In this step,
The enumeration code is defined in the EnumDrivers function. This function has been defined to modularise the code. The source code to enumerate the drivers is shown in figure 9.
LPDIRECTDRAW lpDD; LPDIRECT3D lpD3D; HRESULT rval; // Create a DirectDraw object and query for the Direct3D interface // to use to enumerate the drivers DirectDrawCreate(NULL, &lpDD, NULL); lpDD->QueryInterface(IID_IDirect3D, (void**) &lpD3D); // Enumerate the drivers lpD3D->EnumDevices(enumDeviceFunc, &aInf.CurrDriver); lpD3D->Release(); lpDD->Release();
Step 4: Create Master Frame and CameraThe source code for this step is shown in figure 10.
lpD3DRM->CreateFrame(NULL, &aInf.scene); lpD3DRM->CreateFrame(aInf.scene, &aInf.camera); aInf.camera->SetPosition(aInf.scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
In this step
Step 5: Create ClipperThe source code for this step is shown in figure 11.
DirectDrawCreateClipper(0, &lpDDClipper, NULL); lpDDClipper->SetHWnd(0, win);
In this step, after creating the master frame, we create the clipper using the DirectDrawCreateClipper method and attach it to the Window using the SetHWnd function
Step 6: Create Direc3DRM DeviceThe source code for this step is shown in figure 12.
GetClientRect(win, &rc); CreateDevAndView(lpDDClipper, aInf.CurrDriver, rc.right, rc.bottom);
The device and viewport creation is defined in the CreateDevAndView function. This function has been defined to modularise the code. In this step, we create the device and the viewport. The device is created by using the CreateDeviceFromClipper method, as shown in figure 13.
lpD3DRM->CreateDeviceFromClipper(lpDDClipper, &aInf.DriverGUID[driver], width, height, &aInf.dev);
The viewport is created using the CreateViewport method, as shown in figure 14.
width = aInf.dev->GetWidth(); height = aInf.dev->GetHeight(); lpD3DRM->CreateViewport(aInf.dev, aInf.camera, 0, 0, width, height, &aInf.view); aInf.view->SetBack(D3DVAL(5000.0));
After creating the viewport, we set the back clipping plane of the viewing frustum to the desired value. We then set the rendering quality and other parameters, like fill mode, lighting state, as shown in figure 15.
aInf.dev->SetShades(32); lpD3DRM->SetDefaultTextureColors(64); lpD3DRM->SetDefaultTextureShades(32); Step 7: Define SceneThe source code for this step is shown in figure 16.
DefineScene(aInf.dev, aInf.view, aInf.scene, aInf.camera);
After creating the device and the viewport, we create the scene to be rendered. To define a scene, we first create the world frame, using the CreateFrame method. We then create a frame for the ambient light to be added to the scene, as shown in figure 17.
LPDIRECT3DRMFRAME lpLightFrame, lpWorldFrame; LPDIRECT3DRMLIGHT lpLight1, lpLight2; LPDIRECT3DRMTEXTURE lpTex; LPDIRECT3DRMWRAP lpWrap; LPDIRECT3DRMMESHBUILDER lpMesh; // create frame lpD3DRM->CreateFrame(lpScene, &lpLightFrame); lpD3DRM->CreateFrame(lpScene, &lpWorldFrame); MakeScnLights(lpScene, lpCamera, lpLightFrame, &lpLight1, &lpLight2); SetScnPositions(lpScene, lpCamera, lpLightFrame, lpWorldFrame); MakeScnMesh(&lpMesh); MakeScnWrap(lpMesh, &lpWrap); AddScnTexture(lpMesh, &lpTex); lpWorldFrame->AddVisual((LPDIRECT3DRMVISUAL) lpMesh); lpLightFrame->Release(); lpWorldFrame->Release(); lpMesh->Release(); lpLight1->Release(); lpLight2->Release(); lpTex->Release(); lpWrap->Release();
After creating the frames, we create the lights to be used in the scene, as shown in figure 18.
lpD3DRM->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(0.9), D3DVAL(0.9), D3DVAL(0.9), lplpLight1); lpLightFrame->AddLight(*lplpLight1); lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1), D3DVAL(0.1), D3DVAL(0.1), lplpLight2); lpScene->AddLight(*lplpLight2);
Here, we create two light sources, one being a directional light source amd the other being the ambient light source. The light sources are created using the CreateLightRGB method and added to the frame using the AddLight method. The directional light source is added to the world frame, while the ambient light source is added to the master frame.
After creating the lights, we set the positions of the different frames, like the world frame, the camera frame, as shown in figure 19. A frame is positioned and oriented with respect to its parent, using the SetPosition and the SetOrientation methods respectively.
lpLightFrame1->SetPosition(lpScene, D3DVAL(2), D3DVAL(0.0), D3DVAL(22)); lpCamera->SetPosition(lpScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)); lpCamera->SetOrientation(lpScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0)); lpWorldFrame->SetPosition(lpScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(15)); lpWorldFrame->SetOrientation(lpScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0)); lpWorldFrame->SetRotation(lpScene, D3DVAL(0.0), D3DVAL(0.1), D3DVAL(0.0), D3DVAL(0.05));
After positioning the frames, we need to create the object, namely the sphere, as shown in figure 20.
lpD3DRM->CreateMeshBuilder(&lpMesh); lpMesh->Load("sphere4.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL); lpMesh->Scale(D3DVAL(2), D3DVAL(2), D3DVAL(2)); lpMesh->SetColorRGB(D3DVAL(1), D3DVAL(1), D3DVAL(1));
To load the sphere, we first need to create the mesh object to hold the sphere data, using the CreateMeshBuilder method. We then use the Load method, to load the sphere data from the file ``sphere4.x''. After loading the data, we can transform the objects as desired. We can also set the various parameters of the object. Here we set the colour of the object using the SetColorRGB method.
After loading the object, we need to specify the texture mapping function to be used while applying the texture to the object, as shown in figure 21. The CreateWrap method is used to specify the texture wrapping to be used for texture mapping.
D3DVALUE miny, maxy, height; D3DRMBOX box; lpMesh->GetBox(&box); maxy = box.max.y; miny = box.min.y; height = maxy - miny; lpD3DRM->CreateWrap(D3DRMWRAP_CYLINDER, NULL, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DDivide(miny, height), D3DVAL(1.0), D3DDivide(-D3DVAL(1.0), height), &lpWrap);
After creating the wrapping function, we need to apply it to the selected object using the Apply method, as shown in figure 22.
D3DVALUE miny, maxy, height; D3DRMBOX box; lpMesh->GetBox(&box); maxy = box.max.y; miny = box.min.y; height = maxy - miny; lpD3DRM->CreateWrap(D3DRMWRAP_CYLINDER, NULL, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DDivide(miny, height), D3DVAL(1.0), D3DDivide(-D3DVAL(1.0), height), &lpWrap);
After applying the texture wrap, we need to load the image to
be used as the texture. A texture image can be loaded
using the LoadTexture method. A texture is a 2D
image, as either in the
lpD3DRM->LoadTexture("hello.bmp", &lpTex); lpMesh->SetTexture(lpTex);
After loading the object, it is added to the frame using the AddVisual method.
Step 8: Display WindowThe source code for this step is shown in figure 24.
ShowWindow(win, cmdshow); UpdateWindow(win);
After creating and initializing the different Direct3D and Windows objects, we display the window using the ShowWindow function, as shown in figure 24.
Step 9: Render LoopThe source code for this step is shown in figure 25.
HRESULT rval; aInf.scene->Move(D3DVAL(1.0)); aInf.view->Clear(); aInf.view->Render(aInf.scene); aInf.dev->Update();
After loading the scene and rendering it for the first time, we need to go into an infinite loop, waiting for events. Events are processed by the window procedure as they happen. If there are no events to be processed, the application will be idle, during which time, we will rotate the object by a fixed amount and re-render the scene, as shown in figure 25.
The complete source of the application in C++, is presented in appendix A.
For another sample, refer [5].
Retained Mode ObjectsThe retained mode provides some standard objects with well defined behaviour. These objects help in reducing the amount of work required to be done by the application and hence help in reducing application development time. We will cover some of the object provided by the retained mode in the following subsections.
Direct3DRMDeviceRepresents a visual display destination. The behaviour of the renderer depends on the type of the output device used for rendering. Though only one device is used, multiple viewports on one device can be defined. On the device, we can set parameters like rendering quality and the colour model used for rendering.
Direct3DRMFaceRepresents one single face in a mesh. A mesh object, represented by a Direct3DRMMesh object, is a collection of these face objects.
Direct3DRMFrameRepresents a frame in the scene. It can be used in many ways. The primary usage is for placing various objects and lights in a scene and manipulating them. A scene as we mentioned earlier, is a hierarchy of frames. One frame can contain many objects, lights and other frames, but each frame has atmost one parent.
Direct3DRMLightRepresents a light source in a scene. A light source on creation, has to be added to a frame. A light source can be any one of the following types:
Direct3DRMMeshRepresents a set of polygonal faces, making up the mesh object to be displayed. It defines a set of vertices and a set of faces using the defined set of vertices.
Direct3DRMMeshBuilderBuilt on top of the Direct3DRMMesh object. It provides a convenient way to access the vertices and faces of the mesh object. It provides methods to load a mesh and also for transforming the mesh by translation, scaling and rotation.
Direct3DRMMeshMaterialRepresents the material properties of an object, which defines how a surface reflects the incident light. This object is used to decide two components of the light, namely the emmissive properties and the specular properties. The brightness of the object on receiving light is determined by a value called the power setting, which determines the sharpness of the reflected highlights. A value of less than 5 for the power setting gives a metallic finish to the object, while a value geater than or equal to 5 gives a plastic finish to the object.
Direct3DRMTexture
Used for texture mapping and is an interface to the
DirectDrawSurface object. The texture used can be a 2D image,
either in the
Direct3DRMViewportDefines how the 3D scene is rendered on a 2D window. It defines a rectangular area on the device for rendering the objects in the scene. To do this, it supports a camera, a viewing frustum and the transformations. It additionally supports picking of objects.
Direct3DRMWrapUsed to calculate the texture coordinates for an object. To create a wrapping function for an object, we need to specify the type of wrapping used, the reference frame and origin, the direction vector, the up vector, a pair of scaling factors and the origin for the texture coordinates. The wrapping function determines how the rasterizer module interprets the texture coordinates. The different types of wrapping functions that can be specifie are:
NoteFor a description of all the objects provided by the retained mode, refer [1] and [2].
Sample ImagesTo conclude this tutorial on the retained mode, we present some images in figures 26, 27 and 28 from a few samples, to illustrate a few visual aspects created using retained mode applications.
Figure 26: Sample Image #1
Figure 27: Sample Image #2
Figure 28: Sample Image #3 Appendix AIn this section, we present the complete source code, using C++ and COM, of the sample application. This sample application was used for illustration purposes, to explain the retained mode in a previous section. Direct3D Retained Mode Sample Application Source Code
SummaryIn this tutorial, we have seen that the retained mode of Direct3D is a higher-level mode and provides a higher-level interface to the 3D requirements of an application. It is built on top of the immediate mode and provides a rendering engine, which uses standard algorithms for the 3D operations in a 3D graphics application.
We covered the various features of the retained mode and the kind of support it provides for application creation. We also covered the steps required to create an application using the retained mode and finally had a look at the different objects provided by the retained mode for application development.
Bipin Patwardhan Fri Jan 2 12:22:09 GMT 1998 |
®1998 Adam Perer. All rights reserved. This site was designed by Adam Perer, head of The DirectX Developer Page This site is hosted by Geocities |