Charlie Calvert's Notes for his Direct3D Talk

By: Charles Calvert

Abstract: Here are the notes for the Direct3D talk I made in 1998 and 1999

Delphi and Direct3D

Introduction

Introduction
Overview
Immediate Mode, Retained Mode and OpenGL
Obtaining DirectX

Overview of 3D programming

Key entities used in 3D Programming
Describing 3D Spaces
Describing 3D Shapes
Moving Shapes in Space
Textures and materials
Lights
Z Buffering
Rendering
Animation

Initializing Direct3D

Initialization Overview
Creating Direct3D
Creating Clipper Objects
Creating a Device
Setting up the Device
Understanding Frames
Creating Scenes
Setting up meshes
Loading Textures
Setting up the lights
The Camera and ViewPort

Advanced Concepts

Doom and Quake in Slow Motion
Summary

Introduction

  • My name is Charlie Calvert
  • I'm a Developer Relations Manager at Borland
  • This talk provides an introduction Direct 3D retained mode programming in Delphi
  • Direct3D is a Microsoft Technology that's part of the multimedia APIs called DirectX.
  • Other branches include DirectDraw, DirectSound, DirectMusic, and DirectAnimation
  • To write me or to get materials form the talk: http://www.borland.com/techvoyage

Overview

  1. The talk begins by talking about 3D programming in the abstract
  2. As soon as the preliminaries are out of the way, I will show Direct3D in action
  3. Then I will describe how to create a Direct3D program.
  4. Most of the talk focuses on the complex chore of initializing Direct3D
  5. Once Direct3D is properly initialized, then most of the hard work is over

Immediate Mode, Retained Mode and OpenGL

  1. Direct3D has two modes: Retained mode and Immediate mode
  2. Immediate mode is a very low level, and difficult to use
  3. It gives you detailed control over your program, and a chance to improve performance
  4. Retained mode is based on a series of relatively high level functions for manipulating 3D objects.
  5. This talk is all about retained mode, and indeed, it is logical choice for many programmers
  6. Immediate Mode is popular with professional gamers because it allows them to take advantage of pre-existing libraries
  7. A third, non-DirectX technology is called OpenGL. It is similar to Direct3D immediate mode
  8. OpenGL is a cross platform technology, while DirectX is Windows only
  9. Most new video cards have hardware support for DirectX

Obtaining Direct3D

  1. Direct3D is part of the DirectX and DirectMedia technologies, all of which are COM based services
  2. You can download these tools from the Microsoft site
  3. The DirectX runtime consists of DLLs in the System or System32 directory
  4. The DLLs are relatively small, and come with Win98, WinNT 4 and Win2000. It is also easy to download the DirectX runtime using Windows Update.
  5. The DirectX SDK is large, but can be downloaded if you have a T1 line. Or you can usually order the CD on the web from www.microsoft.com/directx for under $10.
  6. The DirectX SDK also comes with the MSDN
  7. The headers you need for C++ development ship with CBuilder
  8. You can update these headers with more recent files from the SDK. There is a Borland lib directory on the SDK CD.
  9. Direct3D retained mode is usually considered part of DirectMedia, so you want the DirectMedia SDK, but the DirectX SDK usually also has the headers. At least in the last round of CDs, DirectMedia had the docs for Direct3D retained mode.
  10. The current version of DirectX is 6.1. 7.0 is scheduled to ship this fall.
  11. Windows NT 4 currently supports only DirectX 3. Windows 2000, currently scheduled to ship in Nov, will support at least DirectX 6.0, probably 7.0

Key Terms used in 3D Programming

  1. Describing 3D Spaces: Origin, Axes, Vectors, Planes, etc.
  2. Describing 3D Shapes: A Mesh made up of vectors that describe faces and normals
  3. Moving Shapes in Space: Translating, rotating and scaling
  4. Textures
  5. Lights
  6. Z Buffering: Don't draw faces not visible to the user
  7. Rendering: Wireframe mode, Flat, Gouroud and Phong shading
  8. Animation
  9. The goal is to find an object framework that will wrap these technologies.

Describing 3D Spaces

  1. In the Direct3D by default the Origin is at the bottom left corner of your screen.
  2. 3D space has three axes: X, Y, and Z.
  3. The positive x-axis points to the right and the positive y-axis points up. Z gets larger as you move away from the viewer, into the screen.
  4. A Point is an infinitely small area in space described by three values, one for each axis
  5. A Vectors is a line between two points.
  6. Planes are flat surfaces that extent infinitely in two directions.
  7. In Direct3D, a face is a triangular area on a plane.
  8. A vertex is a point used to describe the corners of a face.

Describing 3D Shapes

  1. You can describe 3D shapes with a mesh, usually built in a third party tool and stored in an X file.
  2. The format for X files is found on the Direct Media SDK CD
  3. A mesh is made up of a series of faces.
  4. Normals are vectors often used to describe the colors of the faces in a mesh.
  5. You control a mesh with the IDirect3DRMMesh or IDirect3DRMMeshBuilder interface.
  6. The IDirect3DRMMeshBuilder interface is the easier to use, but it is slower.

Moving Shapes in Space

  1. The act of moving a 3D space in Direct3D is called a translation.
  2. If you change the size of a 3D object, then you are scaling it.
  3. To Turn an object in Direct3D you rotate it.
  4. There are several ways to do this sort of thing, but you often use the ID3DMeshBuilder or ID3DMeshBuilderFrame.

Textures and materials

  1. These are bitmaps painted on a mesh.
  2. Textures are what Wolfenstein, Doom and Quake are all about. In fact they are central to most modern games.
  3. You can scale, wrap and animate textures.
  4. Use the IDirect3DRMTexture and the IDirect3DRmTextureWrap interface
  5. A material controls whether an object is shiney, dull, or appears to emit light.
  6. Use the IDirect3DRMMaterial interface to control this feature.

Lights

  1. Lights can be colored, or they can be just plain white ramp lights.
  2. Ambient lights illuminate an entire scene. This tends to create a very flat look.
  3. A point light emanates in all directions from one location
  4. Directional lights come from one direction, but have no particular origin
  5. Spot lights produce light in the shape of cone
  6. Lighting is much more important than it might appear at first. A scene with good lighting is many times better than an ordinary scene lit with ambient light.
  7. IDirect3DRMLight IDirect3DRMFrame are the key objects when working with lights.

Z Buffering

  1. This is a technique to make sure time is not wasted drawing surfaces that are never shown to the user.
  2. Rendering is performed in front to back order, so hidden surfaces are never drawn.
  3. A program running in 800X600 16 bit mode requires a megabyte of memory.
  4. You need to conserve memory whenever possible.

Rendering

  1. WireFrame mode draws only the edges of a face.
  2. Unlit scenes render quickly but objects appear flat and lifeless.
  3. Flat mode renders a mesh with each face completely flat. It uses face normals.
  4. Gouraud shading uses vertex normals and then light intensities are averaged over the face.
  5. Phong shading uses vertex normals, and calculates light intensities over the face.

Animation

  1. Motion attributes will allow you to translate, rotate, or scale an object.
  2. In key-framing you declare the starting and ending points of a path.
  3. The computer then moves the object from point A to B over a vector or spline path.
  4. You can do this with DirectD3RMAnimation and Directe3DRAnimation set.

Initialization Overview

  1. I'm now going to step you through the process of getting a Direct3D program up and running
  2. Most of this code you can write once and then reuse
  3. It is important that you understand the code, however, as it will need to be tweaked to meet you needs

Creating Direct3D

  1. Call Set8087CW(MCW_EM) to make sure floating point unit does not raise exceptions
  2. Call Direct3DRMCreate to create an instance of the Direct3D Object
  3. Direct3D is of type IDirect3DRM. Like all of Direct3D, it is a COM object
  4. The RM is IDirect3DRM stands for Retained Mode
  5. IDirect3DRM is declared in d3drmobj.h

  CODE: 
    var
      hr: HResult;
      FDirect3D: IDirect3DRM;
      
    begin
      hr := Direct3DRMCreate(FDirect3D);
      if hr <> D3DRM_OK then
        Exception.Create('Direct3DRMCreate failed');
      ...
    end;
      

  ALTERNATE
    
    uses
      ComObj;
    
    OleCheck(Direct3DRMCreate(FDirect3D));
  

  DECLARATION:
  
    STDAPI Direct3DRMCreate(LPDIRECT3DRM FAR *lplpDirect3DRM);


Creating Clipper Objects

  1. Call DirectDrawCreateClipper to create a clipper object
  2. You must have a clipper object, as you will use it to create a Device
  3. Clippers insure that you don't waste time drawing vertices that are off screen
  4. The dwFlags parameter must be zero. pUnkOuter must be nil.

  CODE:
  
    begin  
      ...
      hr := DirectDrawCreateClipper(0, FD3DClipper, nil);
      if(hr <> D3DRM_OK) then
        ShowMessage('DirectDrawCreateClipper failed');
      FD3DClipper.SetHWnd(0, FHandle);
    end;


  DECLARATION:
  
    DirectDrawCreateClipper : function (dwFlags: DWORD;
      out lplpDDClipper: IDirectDrawClipper;
      pUnkOuter: IUnknown) : HResult; stdcall;

Creating a Device

  1. Retained Mode supports devices that render directly to the screen, to windows, or into application memory.
  2. The device itself is virtual
  3. You can define what you want the device to do, for instance, you can set the bit depth and quality.
  4. DirectX will tell you if you are trying to create a device not supported by the current system
  5. By passing in nil in the second parameter we are saying that we want Windows to choose the device.
  6. Use Direct3D.FindDevice if you want to specify the device
  7. Call CreateDeviceFromClipper to return an IDirect3DRMDevice in the last parameter
  8. See also: CreateDeviceFromD3D or CreateDeviceFromSurface.
  

  CODE:
  
    hr := FDirect3D.CreateDeviceFromClipper(FD3DClipper, nil, AClientWidth,
      AClientHeight, FD3Device);
    if(hr <> D3DRM_OK) then
      ShowMessage('CreateDeviceFromClipper failed');

Setting up the Device and Direct3D object

  1. Set the Quality, Shades, TextureColors, TextureShapes and whether or not you want to dither.
  2. Notice that we set the Quality to Gouraud rather than WireFrame, Flat, Unlit or Phong
  3. See the online docs for more info on the IDirect3DRMDevice interface.
  CODE:
    var
      ColorModel: TD3DColorModel;  
      Bpp: Integer;
      
    begin
      FD3Device.SetQuality(D3DRMRENDER_GOURAUD);

      BitsPerPixel := GetDeviceCaps(Canvas.Handle, BITSPIXEL);
      ColorModel := FD3Device.GetColorModel;
      
      case (BitsPerPixel) of
        8: FD3Device.SetDither(false);

        16: begin
          if ColorModel = D3DColor_Mono then    
            FD3Device.SetShades(32);
          FDirect3D.SetDefaultTextureColors(64);
          FDirect3D.SetDefaultTextureShades(32);
          FD3Device.SetDither(false);
        end;

        24, 32: begin
          if ColorModel = D3DColor_Mono then
            FD3Device.SetShades(256);
          FDirect3D.SetDefaultTextureColors(64);
          FDirect3D.SetDefaultTextureShades(256);
          FD3Device.SetDither(false);
        end;
      end;
    end;

Understanding Frames

  1. We are now going to create a scene, and then load some objects, such as a mesh, a camera, and some lights, into it.
  2. The scene will be of type IDirect3DRMFrame, as will the camera.
  3. A third frame will hold the mesh itself.
  4. The term frame is meant to be used in the same context as the word "frame" in the English phrase "frame of reference."
  5. Frames are located in space, and they can have a rotation, a velocity, and they can be moved, or "transformed".
  6. The root frame, which in this case is the scene, has no parent. All other frames in this program have a parent.
  7. If you move a parent frame, then the child frames move with it
  8. The child uses the parent as it's "frame of reference"
  9. For example, a camera sits in a frame. If you want to move or turn the camera, act directly on its frame.

Creating Scenes

  1. The first frame you create in your program is the scene.
  2. The scene has no parent. We specify this fact by passing in nil in the first parameter to CreateFrame:
  3. No Parent: FDirect3D.CreateFrame(nil, FD3DScene);
  4. To give a frame a parent, pass in the parent frame in the first parameter to CreateFrame
  5. With Parent: FDirect3D.CreateFrame(FD3DScene, SomeFrame);
  6. Set the background color for your scene: SetSceneBackgroundRGB
  7. If a parent frame moves, then its children move with it
  8. You can use AddChild to add and remove frames
  CODE:
  
    function T3DTypes.CreateScene(MeshName: string): Boolean;
    var
      hr: HResult;
    begin
      //Create frame for D3DScene as root
      hr := FDirect3D.CreateFrame(nil, FD3DScene);
      if(hr <> D3DRM_OK) then
        ShowMessage('CreateFrame failed');

      Result := False;
      FD3DScene.SetSceneBackgroundRGB(1.0, 1.0, 1.0);
      if not (LoadMesh2(MeshName)) then
        Exit;

      FD3DTextureWrapType := D3DRMWRAP_FLAT;
      if not (LoadWrapTexture()) then
        Exit;

      Result := true;
    end;

Setting up meshes

  1. Use an IDirect3DRMMeshBuilder object to create a mesh.
  2. Use the MeshBuilder to load, scale and set the perspective for the mesh
  3. Then create an IDirect3DRMFrame as a child of the scene to house the Mesh
  4. Use the AddVisual method to add the MeshBuilder to your frame.
  5. In this case, we want the mesh to rotate, so use the SetRotation method of the frame.
  CODE:
    
    FDirect3D.CreateMeshBuilder(F3DTypes.FD3DMeshBuilder);
    FD3DMeshBuilder.Load(PChar(MeshName), nil, 
      D3DRMLOAD_FROMFILE, nil, nil); 
    FD3DMeshBuilder.SetPerspective(true);
    // I don't explain this call in this talk
    ScaleMesh(F3DTypes.FD3DMeshBuilder, 25);

    FDirect3D.CreateFrame(FD3DScene, FD3DMeshBuilderFrame);
    FD3DMeshBuilderFrame.SetRotation(FD3DScene, 1, 0, 0, 0.1);
    FD3DMeshBuilderFrame.AddVisual(FD3DMeshBuilder);

Setting up Textures

  1. Use the IDirect3DRM object to load a texture into an IDirect3DRMTexture object.
  2. Set the texture in the MeshBuilder (not the MeshFrame!)
  3. We won't use this IDirect3DRMTexture object again, so free it.
    
  CODE:

    var
      OpenDlg: TOpenDialog;
      D3DTexture: IDirect3DRMTexture;
    begin
      if OpenDlg.Execute then begin
        FDirect3D.LoadTexture(PChar(OpenDialog.FileName), D3DTexture);
        FD3DMeshBuilder.SetTexture(D3DTexture);
        FD3DMeshBuilder.SetPerspective(true);
        //D3DTexture.Release();
        D3DTexture := nil;
      end;
      Result := true;
    end;

Setting up the Lights

  1. An ambient light comes from all directions at once
  2. There are also spot lights, point source lights, parallel point lighting and directional lights
  CODE:

    function T3DTypes.CreateLight: Boolean;
    var
      D3DAmbientLight: IDirect3DRMLight;
      D3DAmbientLightFrame: IDirect3DRMFrame;
    begin
      //Create an ambient white light
      FDirect3D.CreateLightRGB(D3DRMLIGHT_AMBIENT, 1, 1, 1, D3DAmbientLight);

      //Create frame for light
      FDirect3D.CreateFrame(FD3DScene,  D3DAmbientLightFrame);
      D3DAmbientLightFrame.AddLight(D3DAmbientLight);
      D3DAmbientLightFrame := nil;
      D3DAmbientLight := nil;
    end;

Creating the Camera and ViewPort

  1. The camera is the eye of the scene
  2. When you play Doom or Quake, you are essentially playing the part of the camera
  3. The ViewPort is a rectangular 2D area of a device into which objects will be rendered
  4. Notice that the ViewPort gets a camera passed into it during creation
  5. All viewports have cameras
  CODE:
  
    function T3DTypes.CreateCameraAndViewport: Boolean;
    begin
        //Create a camera
      FDirect3D.CreateFrame(FD3DScene, FD3DCamera);
      FD3DCamera.SetPosition(FD3DScene, 0, 0, -50);

      //Create Viewport
      FDirect3D.CreateViewport(FD3Device, FD3DCamera,
        0, 0, FD3Device.GetWidth(), FD3Device.GetHeight(), FD3DViewPort);

      Result := True;
    end;

Traveling Through a 3D Scene

  1. Games like Doom and Quake work by moving the camera through a scene
  2. The code shown here moves the camera through a scene
  3. First you get the orientation of the camera, then you set its velocity along that vector
  4. If the user wants to look to his left or right, use the SetRotation routine
  CODE:
  
    procedure TSteerShip.Steer(Key: Integer);
    var
      direction, up: D3DVECTOR;
    begin
      FCamera.GetOrientation(FScene, direction, up);
      case (Key) of
        VK_UP: FCamera.SetVelocity(FScene, direction.x, direction.y, direction.z, False);
        VK_DOWN: FCamera.SetVelocity(FScene, -direction.x, -direction.y, -direction.z, False);
        VK_RIGHT: FCamera.SetRotation(FScene, 0.0, 1.0, 0.0, 0.1);
        VK_LEFT: FCamera.SetRotation(FScene, 0.0, 1.0, 0.0, -0.1);
      end;
    end;

Summary

  1. This talk provided an overview of the 3D programming, and how to Initialize Direct3D
  2. My web site: http://www.borland.com/techvoyage
  3. Recommended Reading:
  4. I learned mostly from the docs and examples that game with the sdk
  5. I have not found a great book on this subject, but the best book I've seen is:
  6. The Awesome Power of Direct3D by Peter Kovach, Manning Publications in Creenwich, CT. (orders@manning.com)
  7. This is not perfect, but it is much better than anything else I've seen

Server Response from: ETNASC02