home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / directx / d3dex1 / d3dex1.c next >
C/C++ Source or Header  |  1997-07-14  |  72KB  |  2,295 lines

  1. /***********************************************************************
  2.  *
  3.  * File :       d3dex1.c
  4.  *
  5.  * Abstract :   A very simple Direct3D example which simply draws a
  6.  *              single, rotating, Gouraud shaded triangle in a fixed
  7.  *              size window.
  8.  *
  9.  *              For code clarity a number of issues have not been
  10.  *              addressed in this sample. For example, full screen
  11.  *              operation, resizing the window, texture mapping are
  12.  *              not included. Furthermore, certain optimizations have
  13.  *              not been included where they would obfuscate the code.
  14.  *              Every attempt has been made to highlight these areas
  15.  *              will extensive comments.
  16.  *
  17.  * Author :     Colin D. C. McCartney
  18.  *
  19.  * Date :       09/04/96
  20.  *
  21.  * Version :    V1.0
  22.  *
  23.  * To do:
  24.  * - Handle DDERR_WRONGMODE
  25.  *
  26.  * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
  27.  ***********************************************************************/
  28.  
  29. /***********************************************************************
  30.  *
  31.  * Include files
  32.  *
  33.  ***********************************************************************/
  34.  
  35. #define  INITGUID
  36. #include <windows.h>
  37. #include <math.h>
  38. #include <assert.h>
  39. #include <ddraw.h>
  40. #include <d3d.h>
  41.  
  42. #include "nowarn.h"
  43. #include "resource.h"
  44.  
  45. #ifdef DEBUG
  46. // Make it possible to get reasonable symbols for poor debuggers
  47. #define static 
  48. #endif
  49.  
  50.  /***********************************************************************
  51.  *
  52.  * Constants
  53.  *
  54.  ***********************************************************************/
  55.  
  56. /*
  57.  * Class name for this application's window class.
  58.  */
  59. #define WINDOW_CLASSNAME      "D3DSample1Class"
  60.  
  61. /*
  62.  * Title for the application's window.
  63.  */
  64. #define WINDOW_TITLE          "D3D Sample 1"
  65.  
  66. /*
  67.  * String to be displayed when the application is paused.
  68.  */
  69. #define PAUSED_STRING         "Paused"
  70.  
  71. /*
  72.  * Half height of the view window.
  73.  */
  74. #define HALF_HEIGHT           D3DVAL(0.5)
  75.  
  76. /*
  77.  * Front and back clipping planes.
  78.  */
  79. #define FRONT_CLIP            D3DVAL(1.0)
  80. #define BACK_CLIP             D3DVAL(1000.0)
  81.  
  82. /*
  83.  * Fixed window size.
  84.  */
  85. #define WINDOW_WIDTH          320
  86. #define WINDOW_HEIGHT         200
  87.  
  88. /*
  89.  * Maximum length of the chosen device name and description of the
  90.  * chosen Direct3D device.
  91.  */
  92. #define MAX_DEVICE_NAME       256
  93. #define MAX_DEVICE_DESC       256
  94.  
  95. /*
  96.  * Amount to rotate per frame.
  97.  */
  98. #define M_PI                  3.14159265359
  99. #define M_2PI                 6.28318530718
  100. #define ROTATE_ANGLE_DELTA    (M_2PI / 300.0)
  101.  
  102. /*
  103.  * Execute buffer contents
  104.  */
  105. #define NUM_VERTICES          3UL
  106. #define NUM_INSTRUCTIONS      6UL
  107. #define NUM_STATES            8UL
  108. #define NUM_PROCESSVERTICES   1UL
  109. #define NUM_TRIANGLES         1UL
  110.  
  111. /***********************************************************************
  112.  *
  113.  * Macro funtions.
  114.  *
  115.  ***********************************************************************/
  116.  
  117. /*
  118.  * Extract the error code from an HRESULT
  119.  */
  120. #define CODEFROMHRESULT(hRes) ((hRes) & 0x0000FFFFUL)
  121.  
  122. /***********************************************************************
  123.  *
  124.  * Global store
  125.  *
  126.  ***********************************************************************/
  127.  
  128. /*
  129.  * Application instance handle (set in WinMain).
  130.  */
  131. static HINSTANCE               hAppInstance              = NULL;
  132.  
  133. /*
  134.  * Running in debug mode?
  135.  */
  136. static BOOL                    fDebug                    = FALSE;
  137.  
  138. /*
  139.  * Is the app. active?
  140.  */
  141. static BOOL                    fActive                   = TRUE;
  142.  
  143. /*
  144.  * Has the app. been suspended?
  145.  */
  146. static BOOL                    fSuspended                = FALSE;
  147.  
  148. /*
  149.  * DirectDraw interfaces
  150.  */
  151. static LPDIRECTDRAW            lpdd                      = NULL;
  152. static LPDIRECTDRAWSURFACE     lpddPrimary               = NULL;
  153. static LPDIRECTDRAWSURFACE     lpddDevice                = NULL;
  154. static LPDIRECTDRAWSURFACE     lpddZBuffer               = NULL;
  155. static LPDIRECTDRAWPALETTE     lpddPalette               = NULL;
  156.  
  157. /*
  158.  * Direct3D interfaces
  159.  */
  160. static LPDIRECT3D              lpd3d                     = NULL;
  161. static LPDIRECT3DDEVICE        lpd3dDevice               = NULL;
  162. static LPDIRECT3DMATERIAL      lpd3dMaterial             = NULL;
  163. static LPDIRECT3DMATERIAL      lpd3dBackgroundMaterial   = NULL;
  164. static LPDIRECT3DVIEWPORT      lpd3dViewport             = NULL;
  165. static LPDIRECT3DLIGHT         lpd3dLight                = NULL;
  166. static LPDIRECT3DEXECUTEBUFFER lpd3dExecuteBuffer        = NULL;
  167.  
  168. /*
  169.  * Direct3D handles
  170.  */
  171. static D3DMATRIXHANDLE         hd3dWorldMatrix           = 0UL;
  172. static D3DMATRIXHANDLE         hd3dViewMatrix            = 0UL;
  173. static D3DMATRIXHANDLE         hd3dProjMatrix            = 0UL;
  174. static D3DMATERIALHANDLE       hd3dSurfaceMaterial       = 0UL;
  175. static D3DMATERIALHANDLE       hd3dBackgroundMaterial    = 0UL;
  176.  
  177. /*
  178.  * Globals used for selecting the Direct3D device. They are
  179.  * globals as it makes it easy for the enumeration callback
  180.  * to read and write from them.
  181.  */
  182. static BOOL                    fDeviceFound              = FALSE;
  183. static DWORD                   dwDeviceBitDepth          = 0UL;
  184. static GUID                    guidDevice;
  185. static char                    szDeviceName[MAX_DEVICE_NAME];
  186. static char                    szDeviceDesc[MAX_DEVICE_DESC];
  187. static D3DDEVICEDESC           d3dHWDeviceDesc;
  188. static D3DDEVICEDESC           d3dSWDeviceDesc;
  189.  
  190. /*
  191.  * The screen coordinates of the client area of the window. This
  192.  * rectangle defines the destination into which we blit to update
  193.  * the client area of the window with the results of the 3D rendering.
  194.  */
  195. static RECT                    rDstRect;
  196.  
  197. /*
  198.  * This rectangle defines the portion of the rendering target surface
  199.  * into which we render. The top left coordinates of this rectangle
  200.  * are always zero and the right and bottom give the size of the
  201.  * viewport.
  202.  */
  203. static RECT                    rSrcRect;
  204.  
  205. /*
  206.  * Angle of rotation of the world matrix.
  207.  */
  208. static double                  dAngleOfRotation          = 0.0;
  209.  
  210. /*
  211.  * Predefined transformations.
  212.  */
  213. static D3DMATRIX d3dWorldMatrix =
  214. {
  215.     D3DVAL( 1.0), D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 0.0),
  216.     D3DVAL( 0.0), D3DVAL( 1.0), D3DVAL( 0.0), D3DVAL( 0.0),
  217.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 1.0), D3DVAL( 0.0),
  218.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 1.0)
  219. };
  220.  
  221. static D3DMATRIX d3dViewMatrix =
  222. {
  223.     D3DVAL( 1.0), D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 0.0),
  224.     D3DVAL( 0.0), D3DVAL( 1.0), D3DVAL( 0.0), D3DVAL( 0.0),
  225.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 1.0), D3DVAL( 0.0),
  226.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 5.0), D3DVAL( 1.0)
  227. };
  228.  
  229. static D3DMATRIX d3dProjMatrix =
  230. {
  231.     D3DVAL( 2.0), D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 0.0),
  232.     D3DVAL( 0.0), D3DVAL( 2.0), D3DVAL( 0.0), D3DVAL( 0.0),
  233.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL( 1.0), D3DVAL( 1.0),
  234.     D3DVAL( 0.0), D3DVAL( 0.0), D3DVAL(-1.0), D3DVAL( 0.0)
  235. };
  236.  
  237. /***********************************************************************
  238.  *
  239.  * Function prototypes
  240.  *
  241.  ***********************************************************************/
  242.  
  243. static void           ReportError(HWND hwnd, int nMessage, HRESULT hRes);
  244. static void           FatalError(HWND hwnd, int nMessage, HRESULT hRes);
  245.  
  246. static DWORD          BitDepthToFlags(DWORD dwBitDepth);
  247. static DWORD          FlagsToBitDepth(DWORD dwFlags);
  248.  
  249. static void           SetPerspectiveProjection(LPD3DMATRIX lpd3dMatrix,
  250.                                                double      dHalfHeight,
  251.                                                double      dFrontClipping,
  252.                                                double      dBackClipping);
  253. static void           SetRotationAboutY(LPD3DMATRIX lpd3dMatrix,
  254.                                         double      dAngleOfRotation);
  255.  
  256. static HRESULT        CreateDirect3D(HWND hwnd);
  257. static HRESULT        ReleaseDirect3D(void);
  258.  
  259. static HRESULT        CreatePrimary(HWND hwnd);
  260. static HRESULT        RestorePrimary(void);
  261. static HRESULT        ReleasePrimary(void);
  262.  
  263. static HRESULT WINAPI EnumDeviceCallback(LPGUID          lpGUID, 
  264.                                          LPSTR           lpszDeviceDesc,
  265.                                          LPSTR           lpszDeviceName,
  266.                                          LPD3DDEVICEDESC lpd3dHWDeviceDesc,
  267.                                          LPD3DDEVICEDESC lpd3dSWDeviceDesc,
  268.                                          LPVOID          lpUserArg);
  269. static HRESULT        ChooseDevice(void);
  270.  
  271. static HRESULT        CreateDevice(DWORD dwWidth, DWORD dwHeight);
  272. static HRESULT        RestoreDevice(void);
  273. static HRESULT        ReleaseDevice(void);
  274.  
  275. static LRESULT        RestoreSurfaces(void);
  276.  
  277. static HRESULT        FillExecuteBuffer(void);
  278. static HRESULT        CreateScene(void);
  279. static HRESULT        ReleaseScene(void);
  280. static HRESULT        AnimateScene(void);
  281.  
  282. static HRESULT        UpdateViewport(void);
  283.  
  284. static HRESULT        RenderScene(void);
  285. static HRESULT        DoFrame(HWND hwnd);
  286. static void           PaintSuspended(HWND hwnd, HDC hdc);
  287.  
  288. static LRESULT        OnMove(HWND hwnd, int x, int y);
  289. static LRESULT        OnSize(HWND hwnd, int w, int h);
  290. static LRESULT        OnPaint(HWND hwnd, HDC hdc, LPPAINTSTRUCT lpps);
  291. static LRESULT        OnIdle(HWND hwnd);
  292.  
  293. LRESULT CALLBACK      WndProc(HWND hwnd, UINT msg,
  294.                               WPARAM wParam, LPARAM lParam);
  295. int PASCAL            WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  296.                               LPSTR lpszCommandLine, int cmdShow);
  297.  
  298. /***********************************************************************
  299.  *
  300.  * Macro functions
  301.  *
  302.  ***********************************************************************/
  303.  
  304. /***********************************************************************/
  305.  
  306. #ifdef _DEBUG
  307. #define ASSERT(x)       assert(x)
  308. #else
  309. #define ASSERT(x)
  310. #endif
  311.  
  312. /***********************************************************************/
  313.  
  314. /*
  315.  * Used to keep the compiler happy about any unused parameters.
  316.  */
  317. #define USE_PARAM(x)    (x) = (x)
  318.  
  319. /***********************************************************************/
  320.  
  321. /***********************************************************************
  322.  *
  323.  * Functions
  324.  *
  325.  ***********************************************************************/
  326.  
  327. /***********************************************************************/
  328.  
  329. /*
  330.  * Report the given error by display a message box.
  331.  */
  332. static void
  333. ReportError(HWND hwnd, int nMessage, HRESULT hRes)
  334. {
  335.     HDC  hdc;
  336.     char szBuffer[256];
  337.     char szMessage[128];
  338.     char szError[128];
  339.     int  nStrID;
  340.  
  341.     /*
  342.      * Turn the animation loop off.
  343.      */
  344.     fSuspended = TRUE;
  345.  
  346.     /*
  347.      * Get the high level error message.
  348.      */
  349.     LoadString(hAppInstance, nMessage, szMessage, sizeof(szMessage));
  350.  
  351.     /*
  352.      * We issue sensible error messages for common run time errors. For
  353.      * errors which are internal or coding errors we simply issue an
  354.      * error number (they should never occur).
  355.      */
  356.     switch (hRes)
  357.     {
  358.         case DDERR_EXCEPTION:        nStrID = IDS_ERR_EXCEPTION;        break;
  359.         case DDERR_GENERIC:          nStrID = IDS_ERR_GENERIC;          break;
  360.         case DDERR_OUTOFMEMORY:      nStrID = IDS_ERR_OUTOFMEMORY;      break;
  361.         case DDERR_OUTOFVIDEOMEMORY: nStrID = IDS_ERR_OUTOFVIDEOMEMORY; break;
  362.         case DDERR_SURFACEBUSY:      nStrID = IDS_ERR_SURFACEBUSY;      break;
  363.         case DDERR_SURFACELOST:      nStrID = IDS_ERR_SURFACELOST;      break;
  364.         case DDERR_WRONGMODE:        nStrID = IDS_ERR_WRONGMODE;        break;
  365.         default:                     nStrID = IDS_ERR_INTERNALERROR;    break;
  366.     }
  367.     LoadString(hAppInstance, nStrID, szError, sizeof(szError));
  368.  
  369.     /*
  370.      * Show the "paused" display.
  371.      */
  372.     hdc = GetDC(hwnd);
  373.     PaintSuspended(hwnd, hdc);
  374.     ReleaseDC(hwnd, hdc);
  375.  
  376.     /*
  377.      * Convert the error code into a string (not very informative but
  378.      * it keeps the code simple).
  379.      */
  380.     wsprintf(szBuffer, "%s\n%s (Error #%d)", szMessage, szError, CODEFROMHRESULT(hRes));
  381.     MessageBox(hwnd, szBuffer, WINDOW_TITLE, MB_OK | MB_APPLMODAL);
  382.     fSuspended = FALSE;
  383. }
  384.  
  385. /***********************************************************************/
  386.  
  387. /*
  388.  * Handle a fatal error. Displays the error message via a message box
  389.  * and then destroys the window.
  390.  */
  391. static void
  392. FatalError(HWND hwnd, int nMessage, HRESULT hRes)
  393. {
  394.     /*
  395.      * Report the error.
  396.      */
  397.     ReportError(hwnd, nMessage, hRes);
  398.     fSuspended = TRUE;
  399.  
  400.     /*
  401.      * And shut down.
  402.      *
  403.      * NOTE: We don't attempt to clean up. That will be done
  404.      * when WM_DESTROY happens.
  405.      */
  406.     DestroyWindow(hwnd);
  407. }
  408.  
  409. /***********************************************************************/
  410.  
  411. /*
  412.  * Converts a bit depth into the appropriate DirectDraw bit depth flag.
  413.  */
  414. static DWORD
  415. BitDepthToFlags(DWORD dwBitDepth)
  416. {
  417.     switch (dwBitDepth)
  418.     {
  419.         case  1UL: return DDBD_1;
  420.         case  2UL: return DDBD_2;
  421.         case  4UL: return DDBD_4;
  422.         case  8UL: return DDBD_8;
  423.         case 16UL: return DDBD_16;
  424.         case 24UL: return DDBD_24;
  425.         case 32UL: return DDBD_32;
  426.         default:   return 0UL;     /* Oh, please... */
  427.     }
  428. }
  429.  
  430. /***********************************************************************/
  431.  
  432. /*
  433.  * Convert bit depth flags to an acutal bit count. Selects the smallest
  434.  * bit count in the mask if more than one flag is present.
  435.  */
  436. static DWORD
  437. FlagsToBitDepth(DWORD dwFlags)
  438. {
  439.     if (dwFlags & DDBD_1)
  440.         return 1UL;
  441.     else if (dwFlags & DDBD_2)
  442.         return 2UL;
  443.     else if (dwFlags & DDBD_4)
  444.         return 4UL;
  445.     else if (dwFlags & DDBD_8)
  446.         return 8UL;
  447.     else if (dwFlags & DDBD_16)
  448.         return 16UL;
  449.     else if (dwFlags & DDBD_24)
  450.         return 24UL;
  451.     else if (dwFlags & DDBD_32)
  452.         return 32UL;
  453.     else
  454.         return 0UL; /* Oh, please... */
  455. }
  456.  
  457. /***********************************************************************/
  458.  
  459. /*
  460.  * Set the given matrix to a perspective transform for the given half
  461.  * height and front and back clipping planes.
  462.  */
  463. static void
  464. SetPerspectiveProjection(LPD3DMATRIX lpd3dMatrix,
  465.                          double      dHalfHeight,
  466.                          double      dFrontClipping,
  467.                          double      dBackClipping)
  468. {
  469.     double dTmp1;
  470.     double dTmp2;
  471.  
  472.     ASSERT(NULL != lpd3dMatrix);
  473.  
  474.     dTmp1 = dHalfHeight / dFrontClipping;
  475.     dTmp2 = dBackClipping / (dBackClipping - dFrontClipping);
  476.  
  477.     lpd3dMatrix->_11 =  D3DVAL(2.0);
  478.     lpd3dMatrix->_12 =  D3DVAL(0.0);
  479.     lpd3dMatrix->_13 =  D3DVAL(0.0);
  480.     lpd3dMatrix->_14 =  D3DVAL(0.0);
  481.     lpd3dMatrix->_21 =  D3DVAL(0.0);
  482.     lpd3dMatrix->_22 =  D3DVAL(2.0);
  483.     lpd3dMatrix->_23 =  D3DVAL(0.0);
  484.     lpd3dMatrix->_24 =  D3DVAL(0.0);
  485.     lpd3dMatrix->_31 =  D3DVAL(0.0);
  486.     lpd3dMatrix->_32 =  D3DVAL(0.0);
  487.     lpd3dMatrix->_33 =  D3DVAL(dTmp1 * dTmp2);
  488.     lpd3dMatrix->_34 =  D3DVAL(dTmp1);
  489.     lpd3dMatrix->_41 =  D3DVAL(0.0);
  490.     lpd3dMatrix->_42 =  D3DVAL(0.0);
  491.     lpd3dMatrix->_43 =  D3DVAL(-dHalfHeight * dTmp2);
  492.     lpd3dMatrix->_44 =  D3DVAL(0.0);
  493. }
  494.  
  495.  
  496. /***********************************************************************/
  497.  
  498. /*
  499.  * Set the given matrix to a rotation about Y transform of the given
  500.  * number of radians.
  501.  */
  502. static void
  503. SetRotationAboutY(LPD3DMATRIX lpd3dMatrix, double dAngleOfRotation)
  504. {
  505.     D3DVALUE dvCos;
  506.     D3DVALUE dvSin;
  507.  
  508.     ASSERT(NULL != lpd3dMatrix);
  509.  
  510.     dvCos = D3DVAL(cos(dAngleOfRotation));
  511.     dvSin = D3DVAL(sin(dAngleOfRotation));
  512.  
  513.     lpd3dMatrix->_11 =  dvCos;
  514.     lpd3dMatrix->_12 =  D3DVAL(0.0);
  515.     lpd3dMatrix->_13 = -dvSin;
  516.     lpd3dMatrix->_14 =  D3DVAL(0.0);
  517.     lpd3dMatrix->_21 =  D3DVAL(0.0);
  518.     lpd3dMatrix->_22 =  D3DVAL(1.0);
  519.     lpd3dMatrix->_23 =  D3DVAL(0.0);
  520.     lpd3dMatrix->_24 =  D3DVAL(0.0);
  521.     lpd3dMatrix->_31 =  dvSin;
  522.     lpd3dMatrix->_32 =  D3DVAL(0.0);
  523.     lpd3dMatrix->_33 =  dvCos;
  524.     lpd3dMatrix->_34 =  D3DVAL(0.0);
  525.     lpd3dMatrix->_41 =  D3DVAL(0.0);
  526.     lpd3dMatrix->_42 =  D3DVAL(0.0);
  527.     lpd3dMatrix->_43 =  D3DVAL(0.0);
  528.     lpd3dMatrix->_44 =  D3DVAL(1.0);
  529. }
  530.  
  531. /***********************************************************************/
  532.  
  533. /*
  534.  * Create the DirectDraw/3D driver object and get DirectDraw and Direct3D
  535.  * interfaces for communicating with that object.
  536.  */
  537. static HRESULT
  538. CreateDirect3D(HWND hwnd)
  539. {
  540.     HRESULT hRes;
  541.  
  542.     ASSERT(NULL == lpdd);
  543.     ASSERT(NULL == lpd3d);
  544.  
  545.     /*
  546.      * Create the DirectDraw/3D driver object and get the DirectDraw
  547.      * interface to that object.
  548.      */
  549.     hRes = DirectDrawCreate(NULL, &lpdd, NULL);
  550.     if (FAILED(hRes))
  551.         return hRes;
  552.  
  553.     /*
  554.      * As we are running in a window set the cooperative level to 
  555.      * normal. Also, to ensure that the palette is realized correctly
  556.      * we need to pass the hwnd of the main window.
  557.      */
  558.     hRes = lpdd->lpVtbl->SetCooperativeLevel(lpdd, hwnd, DDSCL_NORMAL);
  559.     if (FAILED(hRes))
  560.         return hRes;
  561.  
  562.     /*
  563.      * Get the Direct3D interface to the DirectDraw/3D driver object.
  564.      */
  565.     hRes = lpdd->lpVtbl->QueryInterface(lpdd, &IID_IDirect3D, &lpd3d);
  566.     if (FAILED(hRes))
  567.         return hRes;
  568.  
  569.     return DD_OK;
  570. }
  571.  
  572. /***********************************************************************/
  573.  
  574. /*
  575.  * Release the DirectDraw/3D driver object.
  576.  */
  577. static HRESULT
  578. ReleaseDirect3D(void)
  579. {
  580.     if (NULL != lpd3d)
  581.     {
  582.         lpd3d->lpVtbl->Release(lpd3d);
  583.         lpd3d = NULL;
  584.     }
  585.     if (NULL != lpdd)
  586.     {
  587.         lpdd->lpVtbl->Release(lpdd);
  588.         lpdd = NULL;
  589.     }
  590.  
  591.     return DD_OK;
  592. }
  593.  
  594. /***********************************************************************/
  595.  
  596. /*
  597.  * Create the primary surface (representing the desktop) and create and
  598.  * attach a clipper and, if necessary, a palette.
  599.  */
  600. static HRESULT
  601. CreatePrimary(HWND hwnd)
  602. {
  603.     HRESULT             hRes;
  604.     DDSURFACEDESC       ddsd;
  605.     LPDIRECTDRAWCLIPPER lpddClipper;
  606.     HDC                 hdc;
  607.     int                 i;
  608.     PALETTEENTRY        peColorTable[256];
  609.  
  610.     ASSERT(NULL != hwnd);
  611.     ASSERT(NULL != lpdd);
  612.     ASSERT(NULL == lpddPrimary);
  613.     ASSERT(NULL == lpddPalette);
  614.  
  615.     /*
  616.      * Create the primary surface.
  617.      */
  618.     ZeroMemory(&ddsd, sizeof(ddsd));
  619.     ddsd.dwSize         = sizeof(ddsd);
  620.     ddsd.dwFlags        = DDSD_CAPS;
  621.     ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  622.     hRes = lpdd->lpVtbl->CreateSurface(lpdd, &ddsd, &lpddPrimary, NULL);
  623.     if (FAILED(hRes))
  624.         return hRes;
  625.  
  626.     /*
  627.      * Create the clipper. We bind the application's window to the
  628.      * clipper and attach it to the primary. This ensures then when we
  629.      * blit from the rendering surface to the primary we don't write
  630.      * outside the visible region of the window.
  631.      */
  632.     hRes = DirectDrawCreateClipper(0UL, &lpddClipper, NULL);
  633.     if (FAILED(hRes))
  634.         return hRes;
  635.     hRes = lpddClipper->lpVtbl->SetHWnd(lpddClipper, 0UL, hwnd);
  636.     if (FAILED(hRes))
  637.     {
  638.         lpddClipper->lpVtbl->Release(lpddClipper);
  639.         return hRes;
  640.     }
  641.     hRes = lpddPrimary->lpVtbl->SetClipper(lpddPrimary, lpddClipper);
  642.     if (FAILED(hRes))
  643.     {
  644.         lpddClipper->lpVtbl->Release(lpddClipper);
  645.         return hRes;
  646.     }
  647.  
  648.     /*
  649.      * We release the clipper interface after attaching it to the surface
  650.      * as we don't need to use it again. The surface holds a reference to
  651.      * the clipper when its been attached. The clipper will therefore be
  652.      * released when the surface is released.
  653.      */
  654.     lpddClipper->lpVtbl->Release(lpddClipper);
  655.  
  656.     /*
  657.      * If the primary is palettized then so will the device (the device
  658.      * surface must have the same pixel format as the current primary if
  659.      * we want to double buffer with DirectDraw). Hence, if the primary
  660.      * is palettized we need to create a palette and attach it to the
  661.      * primary (and to the device surface when we create it).
  662.      */
  663.     ZeroMemory(&ddsd, sizeof(ddsd));
  664.     ddsd.dwSize = sizeof(ddsd);
  665.     hRes = lpddPrimary->lpVtbl->GetSurfaceDesc(lpddPrimary, &ddsd);
  666.     if (FAILED(hRes))
  667.         return hRes;
  668.     if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
  669.     {
  670.         /*
  671.          * Initializing the palette correctly is essential. We are
  672.          * running in a window so we need to be a good windows app
  673.          * and not mess with the top ten and bottom ten static
  674.          * colors. Therefore, we copy them from the system palette
  675.          * and mark them as read only (D3DPAL_READONLY). The middle
  676.          * 236 entries are free for use by Direct3D so we mark them
  677.          * free (D3DPAL_FREE).
  678.          *
  679.          * NOTE: In order that the palette entries are correctly
  680.          * allocated it is essential that the free entries are
  681.          * also marked reserved to GDI (PC_RESERVED).
  682.          *
  683.          * NOTE: We don't need to specify the palette caps flag
  684.          * DDPCAPS_INITIALIZE. This flag is obsolete. CreatePalette
  685.          * must be given a valid palette entry array and always
  686.          * initializes from it.
  687.          */
  688.         hdc = GetDC(NULL);
  689.         GetSystemPaletteEntries(hdc, 0, 256, peColorTable);
  690.         ReleaseDC(NULL, hdc);
  691.  
  692.         for (i = 0; i < 10; i++)
  693.             peColorTable[i].peFlags = D3DPAL_READONLY;
  694.         for (i = 10; i < 246; i++)
  695.             peColorTable[i].peFlags = D3DPAL_FREE | PC_RESERVED;
  696.         for (i = 246; i < 256; i++)
  697.             peColorTable[i].peFlags = D3DPAL_READONLY;
  698.         hRes = lpdd->lpVtbl->CreatePalette(lpdd,
  699.                                            DDPCAPS_8BIT,
  700.                                            peColorTable,
  701.                                            &lpddPalette,
  702.                                            NULL);
  703.         if (FAILED(hRes))
  704.             return hRes;
  705.  
  706.         hRes = lpddPrimary->lpVtbl->SetPalette(lpddPrimary, lpddPalette);
  707.             return hRes;
  708.     }
  709.  
  710.     return DD_OK;
  711. }
  712.  
  713. /***********************************************************************/
  714.  
  715. /*
  716.  * Attempt to restore the video memory allocated for the primary. This
  717.  * function will be invoked by a DirectX function returning
  718.  * DDERR_SURFACELOST due to a mode switch or fullscreen DOS box
  719.  * invalidating video memory.
  720.  */
  721. static HRESULT
  722. RestorePrimary(void)
  723. {
  724.     ASSERT(NULL != lpddPrimary);
  725.  
  726.     return lpddPrimary->lpVtbl->Restore(lpddPrimary);
  727. }
  728.  
  729. /***********************************************************************/
  730.  
  731. /*
  732.  * Release the primary surface and its attached clipper and palette.
  733.  */
  734. static HRESULT
  735. ReleasePrimary(void)
  736. {
  737.     if (NULL != lpddPalette)
  738.     {
  739.         lpddPalette->lpVtbl->Release(lpddPalette);
  740.         lpddPalette = NULL;
  741.     }
  742.     if (NULL != lpddPrimary)
  743.     {
  744.         lpddPrimary->lpVtbl->Release(lpddPrimary);
  745.         lpddPrimary = NULL;
  746.     }
  747.  
  748.     return DD_OK;
  749. }
  750.  
  751. /***********************************************************************/
  752.  
  753. /*
  754.  * This callback is invoked for each Direct3D device installed on the
  755.  * system. For each device we get its identifying GUID, a name and
  756.  * description, a description of its hardware and software capabilities
  757.  * and a user argument (which we don't use).
  758.  */
  759. static HRESULT WINAPI
  760. EnumDeviceCallback(LPGUID          lpGUID, 
  761.                    LPSTR           lpszDeviceDesc,
  762.                    LPSTR           lpszDeviceName,
  763.                    LPD3DDEVICEDESC lpd3dHWDeviceDesc,
  764.                    LPD3DDEVICEDESC lpd3dSWDeviceDesc,
  765.                    LPVOID          lpUserArg)
  766. {
  767.     BOOL            fIsHardware;
  768.     LPD3DDEVICEDESC lpd3dDeviceDesc;
  769.  
  770.     /*
  771.      * We don't use the user argument so just keep the compiler happy.
  772.      */
  773.     USE_PARAM(lpUserArg);
  774.  
  775.     /*
  776.      * If there is no hardware support then the color model is zero.
  777.      */
  778.     fIsHardware     = (0UL != lpd3dHWDeviceDesc->dcmColorModel);
  779.     lpd3dDeviceDesc = (fIsHardware ? lpd3dHWDeviceDesc : lpd3dSWDeviceDesc);
  780.  
  781.     /*
  782.      * If we are in debug mode and this is a hardware device skip it.
  783.      */
  784.     if (fDebug && fIsHardware)
  785.         return D3DENUMRET_OK;
  786.  
  787.     /*
  788.      * Does the device render at the depth we want?
  789.      */
  790.     if (0UL == (lpd3dDeviceDesc->dwDeviceRenderBitDepth & dwDeviceBitDepth))
  791.     {
  792.         /*
  793.          * No skip this device.
  794.          */
  795.         return D3DENUMRET_OK;
  796.     }
  797.  
  798.     /*
  799.      * The device must support gouraud shaded triangles.
  800.      */
  801.     if (D3DCOLOR_MONO == lpd3dDeviceDesc->dcmColorModel)
  802.     {
  803.         if (!(lpd3dDeviceDesc->dpcTriCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDMONO))
  804.         {
  805.             /*
  806.              * No gouraud shading. Skip this device.
  807.              */
  808.             return D3DENUMRET_OK;
  809.         }
  810.     }
  811.     else
  812.     {
  813.         if (!(lpd3dDeviceDesc->dpcTriCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDRGB))
  814.         {
  815.             /*
  816.              * No gouraud shading. Skip this device.
  817.              */
  818.             return D3DENUMRET_OK;
  819.         }
  820.     }
  821.  
  822.     if (!fIsHardware && fDeviceFound && (D3DCOLOR_RGB == lpd3dDeviceDesc->dcmColorModel))
  823.     {
  824.         /*
  825.          * If this is software RGB and we already have found a software
  826.          * mono already then we are not interested. Skip it.
  827.          */
  828.         return D3DENUMRET_OK;
  829.     }
  830.  
  831.     /*
  832.      * This is a device we are interested in - cache the details away.
  833.      */
  834.     fDeviceFound = TRUE;
  835.     CopyMemory(&guidDevice, lpGUID, sizeof(GUID));
  836.     strcpy(szDeviceDesc, lpszDeviceDesc);
  837.     strcpy(szDeviceName, lpszDeviceName);
  838.     CopyMemory(&d3dHWDeviceDesc, lpd3dHWDeviceDesc, sizeof(D3DDEVICEDESC));
  839.     CopyMemory(&d3dSWDeviceDesc, lpd3dSWDeviceDesc, sizeof(D3DDEVICEDESC));
  840.  
  841.     /*
  842.      * If this is a hardware device we have found what we are looking
  843.      * for.
  844.      */
  845.     if (fIsHardware)
  846.         return D3DENUMRET_CANCEL;
  847.  
  848.     /*
  849.      * Keep looking...
  850.      */
  851.     return D3DENUMRET_OK;
  852. }
  853.                             
  854. /***********************************************************************/
  855.  
  856. /*
  857.  * Choose an appropriate Direct3D using the following mechanism:
  858.  *
  859.  * 1) Discard any devices which don't match the current display depth.
  860.  * 2) Discard any devices which can't do gouraud shaded triangles.
  861.  * 3) If a hardware device is found which matches 1) and 2) use it.
  862.  *    However, if we are running in debug mode we will skip hardware.
  863.  * 4) Otherwise favour Mono/Ramp mode software renderers over RGB ones
  864.  *    as, at least until MMX is widespread, Mono will be faster.
  865.  *
  866.  * The actual implementation of this mechanism is in the callback
  867.  * function above.
  868.  */
  869. static HRESULT
  870. ChooseDevice(void)
  871. {
  872.     DDSURFACEDESC ddsd;
  873.     HRESULT       hRes;
  874.  
  875.     ASSERT(NULL != lpd3d);
  876.     ASSERT(NULL != lpddPrimary);
  877.  
  878.     /*
  879.      * As we are running in a window we will not be changing the screen
  880.      * depth and hence the pixel format of the rendering target must match
  881.      * the pixel format of the current primary. Therefore, we need to
  882.      * determine the pixel format of the primary.
  883.      */
  884.     ZeroMemory(&ddsd, sizeof(ddsd));
  885.     ddsd.dwSize = sizeof(ddsd);
  886.     hRes = lpddPrimary->lpVtbl->GetSurfaceDesc(lpddPrimary, &ddsd);
  887.     if (FAILED(hRes))
  888.         return hRes;
  889.  
  890.     dwDeviceBitDepth = BitDepthToFlags(ddsd.ddpfPixelFormat.dwRGBBitCount);
  891.  
  892.     /*
  893.      * Enumerate the devices and pick one.
  894.      */
  895.     fDeviceFound = FALSE;
  896.     hRes = lpd3d->lpVtbl->EnumDevices(lpd3d, EnumDeviceCallback, &fDeviceFound);
  897.     if (FAILED(hRes))
  898.         return hRes;
  899.  
  900.     if (!fDeviceFound)
  901.     {
  902.         /*
  903.          * No suitable device was found. We have no alternative but to
  904.          * fail creation entirely.
  905.          */
  906.         return DDERR_NOTFOUND;
  907.     }
  908.  
  909.     return DD_OK;
  910. }
  911.  
  912. /***********************************************************************/
  913.  
  914. /*
  915.  * Create an instance of the Direct3D device we choose earlier with the
  916.  * given width and height.
  917.  *
  918.  * This function handles all aspects of the device creation including
  919.  * choosing surface memory type, create the device surface, the z-buffer
  920.  * (if necessary) and attaching the palette (if required).
  921.  */
  922. static HRESULT
  923. CreateDevice(DWORD dwWidth, DWORD dwHeight)
  924. {
  925.     LPD3DDEVICEDESC lpd3dDeviceDesc;
  926.     DWORD           dwDeviceMemType;
  927.     DWORD           dwZBufferMemType;
  928.     DDSURFACEDESC   ddsd;
  929.     HRESULT         hRes;
  930.     DWORD           dwZBufferBitDepth;
  931.  
  932.     ASSERT(NULL != lpdd);
  933.     ASSERT(NULL != lpd3d);
  934.     ASSERT(NULL != lpddPrimary);
  935.     ASSERT(NULL == lpddDevice);
  936.     ASSERT(NULL == lpd3dDevice);
  937.  
  938.     /*
  939.      * The first step is to determine the kind of memory (system or
  940.      * video) from which the device surface should be allocated.
  941.      */
  942.     if (0UL != d3dHWDeviceDesc.dcmColorModel)
  943.     {
  944.         lpd3dDeviceDesc = &d3dHWDeviceDesc;
  945.  
  946.         /*
  947.          * Device has a hardware rasterizer. Currently this means that
  948.          * the device surface must be in video memory.
  949.          */
  950.         dwDeviceMemType  = DDSCAPS_VIDEOMEMORY;
  951.         dwZBufferMemType = DDSCAPS_VIDEOMEMORY;
  952.     }
  953.     else
  954.     {
  955.         lpd3dDeviceDesc = &d3dSWDeviceDesc;
  956.  
  957.         /*
  958.          * Device has a software rasterizer. We will let DirectDraw
  959.          * decide where the device surface resides unless we are
  960.          * running in debug mode in which case we will force it into
  961.          * system memory. For a software rasterizer the z-buffer should
  962.          * always go into system memory. A z-buffer in video memory will
  963.          * kill performance.
  964.          */
  965.         dwDeviceMemType  = (fDebug ? DDSCAPS_SYSTEMMEMORY : 0UL);
  966.         dwZBufferMemType = DDSCAPS_SYSTEMMEMORY;
  967.     }
  968.  
  969.     /*
  970.      * Create the device surface. The pixel format will be identical
  971.      * to the primary so we don't have to explicitly specify it. We do
  972.      * need to explicity specify the size, memory type and capabilities
  973.      * of the surface.
  974.      */
  975.     ZeroMemory(&ddsd, sizeof(ddsd));
  976.     ddsd.dwSize         = sizeof(ddsd);
  977.     ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
  978.     ddsd.dwWidth        = dwWidth;
  979.     ddsd.dwHeight       = dwHeight;
  980.     ddsd.ddsCaps.dwCaps = DDSCAPS_3DDEVICE | DDSCAPS_OFFSCREENPLAIN | dwDeviceMemType;
  981.     hRes = lpdd->lpVtbl->CreateSurface(lpdd, &ddsd, &lpddDevice, NULL);
  982.     if (FAILED(hRes))
  983.         return hRes;
  984.  
  985.     /*
  986.      * If we have created a palette then we have already determined that
  987.      * the primary (and hence the device surface) is palettized so 
  988.      * attach the palette to the device surface (its already attached to
  989.      * the primary).
  990.      */
  991.     if (NULL != lpddPalette)
  992.     {
  993.         hRes = lpddDevice->lpVtbl->SetPalette(lpddDevice, lpddPalette);
  994.         if (FAILED(hRes))
  995.             return hRes;
  996.     }
  997.  
  998.     /*
  999.      * We now determine whether we need a z-buffer or not and if so
  1000.      * its bit depth. 
  1001.      */
  1002.     if (0UL != lpd3dDeviceDesc->dwDeviceZBufferBitDepth)
  1003.     {
  1004.         /*
  1005.          * The device supports z-buffering. Determine the depth. We
  1006.          * select the lowest supported z-buffer depth to save memory.
  1007.          * Accuracy is not too important for this sample.
  1008.          */
  1009.         dwZBufferBitDepth = FlagsToBitDepth(lpd3dDeviceDesc->dwDeviceZBufferBitDepth);
  1010.  
  1011.         /*
  1012.          * Create the z-buffer.
  1013.          */
  1014.         ZeroMemory(&ddsd, sizeof(ddsd));
  1015.         ddsd.dwSize            = sizeof(ddsd);
  1016.         ddsd.dwFlags           = DDSD_CAPS   |
  1017.                                  DDSD_WIDTH  |
  1018.                                  DDSD_HEIGHT |
  1019.                                  DDSD_ZBUFFERBITDEPTH;
  1020.         ddsd.ddsCaps.dwCaps    = DDSCAPS_ZBUFFER | dwZBufferMemType;
  1021.         ddsd.dwWidth           = dwWidth;
  1022.         ddsd.dwHeight          = dwHeight;
  1023.         ddsd.dwZBufferBitDepth = dwZBufferBitDepth;
  1024.         hRes = lpdd->lpVtbl->CreateSurface(lpdd, &ddsd, &lpddZBuffer, NULL);
  1025.         if (FAILED(hRes))
  1026.             return hRes;
  1027.  
  1028.         /*
  1029.          * Attach it to the rendering target.
  1030.          */
  1031.         hRes = lpddDevice->lpVtbl->AddAttachedSurface(lpddDevice, lpddZBuffer);
  1032.         if (FAILED(hRes))
  1033.             return hRes;
  1034.     }
  1035.  
  1036.     /*
  1037.      * Now all the elements are in place (device surface in correct
  1038.      * memory type, attached z-buffer of correct depth and memory
  1039.      * type, and palette if necessary) we can actually query for the
  1040.      * Direct3D we choose earlier.
  1041.      */
  1042.     hRes = lpddDevice->lpVtbl->QueryInterface(lpddDevice,
  1043.                                               &guidDevice,
  1044.                                               &lpd3dDevice);
  1045.     if (FAILED(hRes))
  1046.         return hRes;
  1047.  
  1048.     return DD_OK;
  1049. }
  1050.  
  1051. /***********************************************************************/
  1052.  
  1053. /*
  1054.  * Restore the video memory for the device surface and z-buffer if it
  1055.  * has been lost.
  1056.  */
  1057. static HRESULT
  1058. RestoreDevice(void)
  1059. {
  1060.     HRESULT hRes;
  1061.  
  1062.     if (NULL != lpddZBuffer)
  1063.     {
  1064.         hRes = lpddZBuffer->lpVtbl->Restore(lpddZBuffer);
  1065.         if (FAILED(hRes))
  1066.             return hRes;
  1067.     }
  1068.  
  1069.     if (NULL != lpddDevice)
  1070.     {
  1071.         hRes = lpddDevice->lpVtbl->Restore(lpddDevice);
  1072.         if (FAILED(hRes))
  1073.             return hRes;
  1074.     }
  1075.  
  1076.     return DD_OK;
  1077. }
  1078.  
  1079. /***********************************************************************/
  1080.  
  1081. /*
  1082.  * Release the Direct3D device and its associated surfaces.
  1083.  */
  1084. static HRESULT
  1085. ReleaseDevice(void)
  1086. {
  1087.     if (NULL != lpd3dDevice)
  1088.     {
  1089.         lpd3dDevice->lpVtbl->Release(lpd3dDevice);
  1090.         lpd3dDevice = NULL;
  1091.     }
  1092.     if (NULL != lpddZBuffer)
  1093.     {
  1094.         lpddZBuffer->lpVtbl->Release(lpddZBuffer);
  1095.         lpddZBuffer = NULL;
  1096.     }
  1097.     if (NULL != lpddDevice)
  1098.     {
  1099.         lpddDevice->lpVtbl->Release(lpddDevice);
  1100.         lpddDevice = NULL;
  1101.     }
  1102.  
  1103.     return DD_OK;
  1104. }
  1105.  
  1106. /***********************************************************************/
  1107.  
  1108. /*
  1109.  * Attempt to restore all the surfaces used by the application.
  1110.  */
  1111. static LRESULT
  1112. RestoreSurfaces(void)
  1113. {
  1114.     HRESULT hRes;
  1115.  
  1116.     hRes = RestorePrimary();
  1117.     if (FAILED(hRes))
  1118.         return hRes;
  1119.  
  1120.     hRes = RestoreDevice();
  1121.     if (FAILED(hRes))
  1122.         return hRes;
  1123.  
  1124.     return DD_OK;
  1125. }
  1126.  
  1127. /***********************************************************************/
  1128.  
  1129. /*
  1130.  * Fill the single execute buffer used in this sample with all the
  1131.  * vertices, transform, light and render state and drawing primitives
  1132.  * necessary to draw our triangle.
  1133.  *
  1134.  * NOTE: This is not the most efficient way of organizing the execute
  1135.  * buffer. For best performance you want to minimize state changes. In
  1136.  * this sample we submit the execute buffer for each frame in the
  1137.  * animation loop and no state in the buffer is modified. The only 
  1138.  * thing we modify is the world matrix (its contents - not its handle).
  1139.  * Therefore, it would be more efficient to extract all the static
  1140.  * state instructions into a separate execute buffer which we issue
  1141.  * once only at startup and, from then on, simply execute a second
  1142.  * execute buffer with vertices and triangles.
  1143.  * However, this sample is not exactly performance critical so we will
  1144.  * just use one execute buffer and resubmit it its entirety for each
  1145.  * frame.
  1146.  */
  1147. static HRESULT
  1148. FillExecuteBuffer(void)
  1149. {
  1150.     HRESULT              hRes;
  1151.     D3DEXECUTEBUFFERDESC d3dExeBufDesc;
  1152.     LPD3DVERTEX          lpVertex;
  1153.     LPD3DINSTRUCTION     lpInstruction;
  1154.     LPD3DPROCESSVERTICES lpProcessVertices;
  1155.     LPD3DTRIANGLE        lpTriangle;
  1156.     LPD3DSTATE           lpState;
  1157.  
  1158.     ASSERT(NULL != lpd3dExecuteBuffer);
  1159.     ASSERT(0UL  != hd3dSurfaceMaterial);
  1160.     ASSERT(0UL  != hd3dWorldMatrix);
  1161.     ASSERT(0UL  != hd3dViewMatrix);
  1162.     ASSERT(0UL  != hd3dProjMatrix);
  1163.  
  1164.     /*
  1165.      * Lock the execute buffer.
  1166.      */
  1167.     ZeroMemory(&d3dExeBufDesc, sizeof(d3dExeBufDesc));
  1168.     d3dExeBufDesc.dwSize = sizeof(d3dExeBufDesc);
  1169.     hRes = lpd3dExecuteBuffer->lpVtbl->Lock(lpd3dExecuteBuffer, &d3dExeBufDesc);
  1170.     if (FAILED(hRes))
  1171.         return hRes;
  1172.  
  1173.     /*
  1174.      * For explanatory purposes we fill the execute buffer by casting
  1175.      * a pointer to the execute buffer to the appropriate data structures.
  1176.      *
  1177.      * !!! NOTE: Issue - alignment.
  1178.      */
  1179.     lpVertex = (LPD3DVERTEX)d3dExeBufDesc.lpData;
  1180.  
  1181.     /*
  1182.      * First vertex.
  1183.      */
  1184.     lpVertex->dvX  = D3DVAL( 0.0); /* Position in model coordinates       */
  1185.     lpVertex->dvY  = D3DVAL( 1.0);
  1186.     lpVertex->dvZ  = D3DVAL( 0.0);
  1187.     lpVertex->dvNX = D3DVAL( 0.0); /* Normalized illumination normal      */
  1188.     lpVertex->dvNY = D3DVAL( 0.0);
  1189.     lpVertex->dvNZ = D3DVAL(-1.0);
  1190.     lpVertex->dvTU = D3DVAL( 0.0); /* Texture coordinates (not used here) */
  1191.     lpVertex->dvTV = D3DVAL( 1.0);
  1192.     lpVertex++;
  1193.  
  1194.     /*
  1195.      * Second vertex.
  1196.      */
  1197.     lpVertex->dvX  = D3DVAL( 1.0); /* Position in model coordinates       */
  1198.     lpVertex->dvY  = D3DVAL(-1.0);
  1199.     lpVertex->dvZ  = D3DVAL( 0.0);
  1200.     lpVertex->dvNX = D3DVAL( 0.0); /* Normalized illumination normal      */
  1201.     lpVertex->dvNY = D3DVAL( 0.0);
  1202.     lpVertex->dvNZ = D3DVAL(-1.0);
  1203.     lpVertex->dvTU = D3DVAL( 1.0); /* Texture coordinates (not used here) */
  1204.     lpVertex->dvTV = D3DVAL( 1.0);
  1205.     lpVertex++;
  1206.  
  1207.     /*
  1208.      * Third vertex.
  1209.      */
  1210.     lpVertex->dvX  = D3DVAL(-1.0); /* Position in model coordinates       */
  1211.     lpVertex->dvY  = D3DVAL(-1.0);
  1212.     lpVertex->dvZ  = D3DVAL( 0.0);
  1213.     lpVertex->dvNX = D3DVAL( 0.0); /* Normalized illumination normal      */
  1214.     lpVertex->dvNY = D3DVAL( 0.0);
  1215.     lpVertex->dvNZ = D3DVAL(-1.0);
  1216.     lpVertex->dvTU = D3DVAL( 1.0); /* Texture coordinates (not used here) */
  1217.     lpVertex->dvTV = D3DVAL( 0.0);
  1218.     lpVertex++;
  1219.  
  1220.     /*
  1221.      * Transform state - world, view and projection.
  1222.      */
  1223.     lpInstruction = (LPD3DINSTRUCTION)lpVertex;
  1224.     lpInstruction->bOpcode = D3DOP_STATETRANSFORM;
  1225.     lpInstruction->bSize   = sizeof(D3DSTATE);
  1226.     lpInstruction->wCount  = 3U;
  1227.     lpInstruction++;
  1228.     lpState = (LPD3DSTATE)lpInstruction;
  1229.     lpState->dtstTransformStateType = D3DTRANSFORMSTATE_WORLD;
  1230.     lpState->dwArg[0] = hd3dWorldMatrix;
  1231.     lpState++;
  1232.     lpState->dtstTransformStateType = D3DTRANSFORMSTATE_VIEW;
  1233.     lpState->dwArg[0] = hd3dViewMatrix;
  1234.     lpState++;
  1235.     lpState->dtstTransformStateType = D3DTRANSFORMSTATE_PROJECTION;
  1236.     lpState->dwArg[0] = hd3dProjMatrix;
  1237.     lpState++;
  1238.  
  1239.     /*
  1240.      * Lighting state.
  1241.      */
  1242.     lpInstruction = (LPD3DINSTRUCTION)lpState;
  1243.     lpInstruction->bOpcode = D3DOP_STATELIGHT;
  1244.     lpInstruction->bSize   = sizeof(D3DSTATE);
  1245.     lpInstruction->wCount  = 2U;
  1246.     lpInstruction++;
  1247.     lpState = (LPD3DSTATE)lpInstruction;
  1248.     lpState->dlstLightStateType = D3DLIGHTSTATE_MATERIAL;
  1249.     lpState->dwArg[0] = hd3dSurfaceMaterial;
  1250.     lpState++;
  1251.     lpState->dlstLightStateType = D3DLIGHTSTATE_AMBIENT;
  1252.     lpState->dwArg[0] = RGBA_MAKE(128, 128, 128, 128);
  1253.     lpState++;
  1254.  
  1255.     /*
  1256.      * Render state.
  1257.      */
  1258.     lpInstruction = (LPD3DINSTRUCTION)lpState;
  1259.     lpInstruction->bOpcode = D3DOP_STATERENDER;
  1260.     lpInstruction->bSize = sizeof(D3DSTATE);
  1261.     lpInstruction->wCount = 3U;
  1262.     lpInstruction++;
  1263.     lpState = (LPD3DSTATE)lpInstruction;
  1264.     lpState->drstRenderStateType = D3DRENDERSTATE_FILLMODE;
  1265.     lpState->dwArg[0] = D3DFILL_SOLID;
  1266.     lpState++;
  1267.     lpState->drstRenderStateType = D3DRENDERSTATE_SHADEMODE;
  1268.     lpState->dwArg[0] = D3DSHADE_GOURAUD;
  1269.     lpState++;
  1270.     lpState->drstRenderStateType = D3DRENDERSTATE_DITHERENABLE;
  1271.     lpState->dwArg[0] = TRUE;
  1272.     lpState++;
  1273.  
  1274.     /*
  1275.      * The process vertices instruction tells the driver what to
  1276.      * do with the vertices in the buffer. In this sample we want
  1277.      * Direct3D to perform the entire pipeline on our behalf so
  1278.      * the instruction is D3DPROCESSVERTICES_TRANSFORMLIGHT.
  1279.      */
  1280.     lpInstruction = (LPD3DINSTRUCTION)lpState;
  1281.     lpInstruction->bOpcode = D3DOP_PROCESSVERTICES;
  1282.     lpInstruction->bSize   = sizeof(D3DPROCESSVERTICES);
  1283.     lpInstruction->wCount  = 1U;
  1284.     lpInstruction++;
  1285.     lpProcessVertices = (LPD3DPROCESSVERTICES)lpInstruction;
  1286.     lpProcessVertices->dwFlags    = D3DPROCESSVERTICES_TRANSFORMLIGHT;
  1287.     lpProcessVertices->wStart     = 0U;           /* First source vertex */
  1288.     lpProcessVertices->wDest      = 0U;
  1289.     lpProcessVertices->dwCount    = NUM_VERTICES; /* Number of vertices  */
  1290.     lpProcessVertices->dwReserved = 0UL;
  1291.     lpProcessVertices++;
  1292.  
  1293.     /*
  1294.      * Draw the triangle.
  1295.      */
  1296.     lpInstruction = (LPD3DINSTRUCTION)lpProcessVertices;
  1297.     lpInstruction->bOpcode = D3DOP_TRIANGLE;
  1298.     lpInstruction->bSize   = sizeof(D3DTRIANGLE);
  1299.     lpInstruction->wCount  = 1U;
  1300.     lpInstruction++;
  1301.     lpTriangle = (LPD3DTRIANGLE)lpInstruction;
  1302.     lpTriangle->wV1    = 0U; 
  1303.     lpTriangle->wV2    = 1U;
  1304.     lpTriangle->wV3    = 2U;
  1305.     lpTriangle->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE;
  1306.     lpTriangle++;
  1307.  
  1308.     /*
  1309.      * Stop execution of the buffer.
  1310.      */
  1311.     lpInstruction = (LPD3DINSTRUCTION)lpTriangle;
  1312.     lpInstruction->bOpcode = D3DOP_EXIT;
  1313.     lpInstruction->bSize   = 0UL;
  1314.     lpInstruction->wCount  = 0U;
  1315.  
  1316.     /*
  1317.      * Unlock the execute buffer.
  1318.      */
  1319.     lpd3dExecuteBuffer->lpVtbl->Unlock(lpd3dExecuteBuffer);
  1320.  
  1321.     return DD_OK;
  1322. }
  1323.  
  1324. /***********************************************************************/
  1325.  
  1326. /*
  1327.  * Create the elements making up the 3D scene.
  1328.  *
  1329.  * In this sample the scene consists of the single light, the viewport,
  1330.  * the background and surface material, the three transformation matrices
  1331.  * and the execute buffer holding the state changes and drawing primitives.
  1332.  */
  1333. static HRESULT
  1334. CreateScene(void)
  1335. {
  1336.     HRESULT              hRes;
  1337.     D3DMATERIAL          d3dMaterial;
  1338.     D3DLIGHT             d3dLight;
  1339.     DWORD                dwVertexSize;
  1340.     DWORD                dwInstructionSize;
  1341.     DWORD                dwExecuteBufferSize;
  1342.     D3DEXECUTEBUFFERDESC d3dExecuteBufferDesc;
  1343.     D3DEXECUTEDATA       d3dExecuteData;
  1344.  
  1345.     ASSERT(NULL != lpd3d);
  1346.     ASSERT(NULL != lpd3dDevice);
  1347.     ASSERT(NULL == lpd3dViewport);
  1348.     ASSERT(NULL == lpd3dMaterial);
  1349.     ASSERT(NULL == lpd3dBackgroundMaterial);
  1350.     ASSERT(NULL == lpd3dExecuteBuffer);
  1351.     ASSERT(NULL == lpd3dLight);
  1352.     ASSERT(0UL  == hd3dWorldMatrix);
  1353.     ASSERT(0UL  == hd3dViewMatrix);
  1354.     ASSERT(0UL  == hd3dProjMatrix);
  1355.  
  1356.     /*
  1357.      * Create the light.
  1358.      */
  1359.     hRes = lpd3d->lpVtbl->CreateLight(lpd3d, &lpd3dLight, NULL);
  1360.     if (FAILED(hRes))
  1361.         return hRes;
  1362.  
  1363.     ZeroMemory(&d3dLight, sizeof(d3dLight));
  1364.     d3dLight.dwSize = sizeof(d3dLight);
  1365.     d3dLight.dltType = D3DLIGHT_POINT;
  1366.     d3dLight.dcvColor.dvR    = D3DVAL( 1.0);
  1367.     d3dLight.dcvColor.dvG    = D3DVAL( 1.0);
  1368.     d3dLight.dcvColor.dvB    = D3DVAL( 1.0);
  1369.     d3dLight.dcvColor.dvA    = D3DVAL( 1.0);
  1370.     d3dLight.dvPosition.dvX  = D3DVAL( 1.0);
  1371.     d3dLight.dvPosition.dvY  = D3DVAL(-1.0);
  1372.     d3dLight.dvPosition.dvZ  = D3DVAL(-1.0);
  1373.     d3dLight.dvAttenuation0  = D3DVAL( 1.0);
  1374.     d3dLight.dvAttenuation1  = D3DVAL( 0.1);
  1375.     d3dLight.dvAttenuation2  = D3DVAL( 0.0);
  1376.     hRes = lpd3dLight->lpVtbl->SetLight(lpd3dLight, &d3dLight);
  1377.     if (FAILED(hRes))
  1378.         return hRes;
  1379.  
  1380.     /*
  1381.      * Create the background material.
  1382.      */
  1383.     hRes = lpd3d->lpVtbl->CreateMaterial(lpd3d, &lpd3dBackgroundMaterial, NULL);
  1384.     if (FAILED(hRes))
  1385.         return hRes;
  1386.  
  1387.     ZeroMemory(&d3dMaterial, sizeof(d3dMaterial));
  1388.     d3dMaterial.dwSize = sizeof(d3dMaterial);
  1389.     d3dMaterial.dcvDiffuse.r  = D3DVAL(0.0);
  1390.     d3dMaterial.dcvDiffuse.g  = D3DVAL(0.0);
  1391.     d3dMaterial.dcvDiffuse.b  = D3DVAL(0.0);
  1392.     d3dMaterial.dcvAmbient.r  = D3DVAL(0.0);
  1393.     d3dMaterial.dcvAmbient.g  = D3DVAL(0.0);
  1394.     d3dMaterial.dcvAmbient.b  = D3DVAL(0.0);
  1395.     d3dMaterial.dcvSpecular.r = D3DVAL(0.0);
  1396.     d3dMaterial.dcvSpecular.g = D3DVAL(0.0);
  1397.     d3dMaterial.dcvSpecular.b = D3DVAL(0.0);
  1398.     d3dMaterial.dvPower       = D3DVAL(0.0);
  1399.  
  1400.     /*
  1401.      * As this is the background material we don't want a ramp allocated (we
  1402.      * are not going to be smooth shading the background).
  1403.      */
  1404.     d3dMaterial.dwRampSize    = 1UL;
  1405.     
  1406.     hRes = lpd3dBackgroundMaterial->lpVtbl->SetMaterial(lpd3dBackgroundMaterial,
  1407.                                                         &d3dMaterial);
  1408.     if (FAILED(hRes))
  1409.         return hRes;
  1410.     hRes = lpd3dBackgroundMaterial->lpVtbl->GetHandle(lpd3dBackgroundMaterial,
  1411.                                                       lpd3dDevice,
  1412.                                                       &hd3dBackgroundMaterial);
  1413.     if (FAILED(hRes))
  1414.         return hRes;
  1415.  
  1416.     /*
  1417.      * Create the viewport.
  1418.      *
  1419.      * The actual viewport parameter are set in the function UpdateViewport
  1420.      * which is called in response to WM_SIZE.
  1421.      */
  1422.     hRes = lpd3d->lpVtbl->CreateViewport(lpd3d, &lpd3dViewport, NULL);
  1423.     if (FAILED(hRes))
  1424.         return hRes;
  1425.     hRes = lpd3dDevice->lpVtbl->AddViewport(lpd3dDevice, lpd3dViewport);
  1426.     if (FAILED(hRes))
  1427.         return hRes;
  1428.     hRes = lpd3dViewport->lpVtbl->SetBackground(lpd3dViewport, hd3dBackgroundMaterial);
  1429.     if (FAILED(hRes))
  1430.         return hRes;
  1431.     hRes = lpd3dViewport->lpVtbl->AddLight(lpd3dViewport, lpd3dLight);
  1432.     if (FAILED(hRes))
  1433.         return hRes;
  1434.  
  1435.     /*
  1436.      * Create the matrices.
  1437.      */
  1438.     hRes = lpd3dDevice->lpVtbl->CreateMatrix(lpd3dDevice, &hd3dWorldMatrix);
  1439.     if (FAILED(hRes))
  1440.         return hRes;
  1441.     hRes = lpd3dDevice->lpVtbl->SetMatrix(lpd3dDevice, hd3dWorldMatrix, &d3dWorldMatrix);
  1442.     if (FAILED(hRes))
  1443.         return hRes;
  1444.     hRes = lpd3dDevice->lpVtbl->CreateMatrix(lpd3dDevice, &hd3dViewMatrix);
  1445.     if (FAILED(hRes))
  1446.         return hRes;
  1447.     hRes = lpd3dDevice->lpVtbl->SetMatrix(lpd3dDevice, hd3dViewMatrix, &d3dViewMatrix);
  1448.     if (FAILED(hRes))
  1449.         return hRes;
  1450.     hRes = lpd3dDevice->lpVtbl->CreateMatrix(lpd3dDevice, &hd3dProjMatrix);
  1451.     if (FAILED(hRes))
  1452.         return hRes;
  1453.     SetPerspectiveProjection(&d3dProjMatrix, HALF_HEIGHT, FRONT_CLIP, BACK_CLIP);
  1454.     hRes = lpd3dDevice->lpVtbl->SetMatrix(lpd3dDevice, hd3dProjMatrix, &d3dProjMatrix);
  1455.     if (FAILED(hRes))
  1456.         return hRes;
  1457.  
  1458.     /*
  1459.      * Create the surface material.
  1460.      */
  1461.     hRes = lpd3d->lpVtbl->CreateMaterial(lpd3d, &lpd3dMaterial, NULL);
  1462.     if (FAILED(hRes))
  1463.         return hRes;
  1464.     ZeroMemory(&d3dMaterial, sizeof(d3dMaterial));
  1465.     d3dMaterial.dwSize = sizeof(d3dMaterial);
  1466.  
  1467.     /*
  1468.      * Base green with white specular.
  1469.      */
  1470.     d3dMaterial.dcvDiffuse.r  = D3DVAL(0.0);
  1471.     d3dMaterial.dcvDiffuse.g  = D3DVAL(1.0);
  1472.     d3dMaterial.dcvDiffuse.b  = D3DVAL(0.0);
  1473.     d3dMaterial.dcvAmbient.r  = D3DVAL(0.0);
  1474.     d3dMaterial.dcvAmbient.g  = D3DVAL(0.4);
  1475.     d3dMaterial.dcvAmbient.b  = D3DVAL(0.0);
  1476.     d3dMaterial.dcvSpecular.r = D3DVAL(1.0);
  1477.     d3dMaterial.dcvSpecular.g = D3DVAL(1.0);
  1478.     d3dMaterial.dcvSpecular.b = D3DVAL(1.0);
  1479.     d3dMaterial.dvPower       = D3DVAL(20.0);
  1480.     d3dMaterial.dwRampSize    = 16UL;
  1481.     
  1482.     hRes = lpd3dMaterial->lpVtbl->SetMaterial(lpd3dMaterial, &d3dMaterial);
  1483.     if (FAILED(hRes))
  1484.         return hRes;
  1485.  
  1486.     hRes = lpd3dMaterial->lpVtbl->GetHandle(lpd3dMaterial, lpd3dDevice, &hd3dSurfaceMaterial);
  1487.     if (FAILED(hRes))
  1488.         return hRes;
  1489.  
  1490.     /*
  1491.      * Build the execute buffer.
  1492.      */
  1493.     dwVertexSize        = (NUM_VERTICES        * sizeof(D3DVERTEX));
  1494.     dwInstructionSize   = (NUM_INSTRUCTIONS    * sizeof(D3DINSTRUCTION))     +
  1495.                           (NUM_STATES          * sizeof(D3DSTATE))           +
  1496.                           (NUM_PROCESSVERTICES * sizeof(D3DPROCESSVERTICES)) +
  1497.                           (NUM_TRIANGLES       * sizeof(D3DTRIANGLE));
  1498.     dwExecuteBufferSize = dwVertexSize + dwInstructionSize;
  1499.     ZeroMemory(&d3dExecuteBufferDesc, sizeof(d3dExecuteBufferDesc));
  1500.     d3dExecuteBufferDesc.dwSize       = sizeof(d3dExecuteBufferDesc);
  1501.     d3dExecuteBufferDesc.dwFlags      = D3DDEB_BUFSIZE;
  1502.     d3dExecuteBufferDesc.dwBufferSize = dwExecuteBufferSize;
  1503.     hRes = lpd3dDevice->lpVtbl->CreateExecuteBuffer(lpd3dDevice,
  1504.                                                     &d3dExecuteBufferDesc,
  1505.                                                     &lpd3dExecuteBuffer,
  1506.                                                     NULL);
  1507.     if (FAILED(hRes))
  1508.         return hRes;
  1509.  
  1510.     /*
  1511.      * Fill the execute buffer with the required vertices, state
  1512.      * instructions and drawing primitives.
  1513.      */
  1514.     hRes = FillExecuteBuffer();
  1515.     if (FAILED(hRes))
  1516.         return hRes;
  1517.  
  1518.     /*
  1519.      * Set the execute data so Direct3D knows how many vertices are in the
  1520.      * buffer and where the instructions start.
  1521.      */
  1522.     ZeroMemory(&d3dExecuteData, sizeof(d3dExecuteData));
  1523.     d3dExecuteData.dwSize = sizeof(d3dExecuteData);
  1524.     d3dExecuteData.dwVertexCount       = NUM_VERTICES;
  1525.     d3dExecuteData.dwInstructionOffset = dwVertexSize;
  1526.     d3dExecuteData.dwInstructionLength = dwInstructionSize;
  1527.     hRes = lpd3dExecuteBuffer->lpVtbl->SetExecuteData(lpd3dExecuteBuffer, &d3dExecuteData);
  1528.     if (FAILED(hRes))
  1529.         return hRes;
  1530.  
  1531.     return DD_OK;
  1532. }
  1533.  
  1534. /***********************************************************************/
  1535.  
  1536. /*
  1537.  * Release all the objects comprising the 3D scene.
  1538.  */
  1539. static HRESULT
  1540. ReleaseScene(void)
  1541. {
  1542.     if (NULL != lpd3dExecuteBuffer)
  1543.     {
  1544.         lpd3dExecuteBuffer->lpVtbl->Release(lpd3dExecuteBuffer);
  1545.         lpd3dExecuteBuffer = NULL;
  1546.     }
  1547.     if (NULL != lpd3dBackgroundMaterial)
  1548.     {
  1549.         lpd3dBackgroundMaterial->lpVtbl->Release(lpd3dBackgroundMaterial);
  1550.         lpd3dBackgroundMaterial = NULL;
  1551.     }
  1552.     if (NULL != lpd3dMaterial)
  1553.     {
  1554.         lpd3dMaterial->lpVtbl->Release(lpd3dMaterial);
  1555.         lpd3dMaterial = NULL;
  1556.     }
  1557.     if (0UL != hd3dWorldMatrix)
  1558.     {
  1559.         lpd3dDevice->lpVtbl->DeleteMatrix(lpd3dDevice, hd3dWorldMatrix);
  1560.         hd3dWorldMatrix = 0UL;
  1561.     }
  1562.     if (0UL != hd3dViewMatrix)
  1563.     {
  1564.         lpd3dDevice->lpVtbl->DeleteMatrix(lpd3dDevice, hd3dViewMatrix);
  1565.         hd3dViewMatrix = 0UL;
  1566.     }
  1567.     if (0UL != hd3dProjMatrix)
  1568.     {
  1569.         lpd3dDevice->lpVtbl->DeleteMatrix(lpd3dDevice, hd3dProjMatrix);
  1570.         hd3dProjMatrix = 0UL;
  1571.     }
  1572.     if (NULL != lpd3dLight)
  1573.     {
  1574.         lpd3dLight->lpVtbl->Release(lpd3dLight);
  1575.         lpd3dLight = NULL;
  1576.     }
  1577.     if (NULL != lpd3dViewport)
  1578.     {
  1579.         lpd3dViewport->lpVtbl->Release(lpd3dViewport);
  1580.         lpd3dViewport = NULL;
  1581.     }
  1582.  
  1583.     return DD_OK;
  1584. }
  1585.  
  1586. /***********************************************************************/
  1587.  
  1588. /*
  1589.  * Animate the scene.
  1590.  *
  1591.  * The animation in this sample is simply a rotation about the Y axis.
  1592.  * So all we need to do is build a rotation matrix and set the world
  1593.  * matrix to that new rotation matrix.
  1594.  *
  1595.  * Note, we don't need to modify the execute buffer in any way to peform
  1596.  * this rotation. We simply set the matrix and resubmit the execute
  1597.  * buffer.
  1598.  */
  1599. static HRESULT
  1600. AnimateScene(void)
  1601. {
  1602.     HRESULT hRes;
  1603.  
  1604.     ASSERT(NULL != lpd3dDevice);
  1605.     ASSERT(0UL  != hd3dWorldMatrix);
  1606.  
  1607.     /*
  1608.      * We rotate the triangle by setting thr world transform to a
  1609.      * rotation matrix.
  1610.      */
  1611.     SetRotationAboutY(&d3dWorldMatrix, dAngleOfRotation);
  1612.     dAngleOfRotation += ROTATE_ANGLE_DELTA;
  1613.     hRes = lpd3dDevice->lpVtbl->SetMatrix(lpd3dDevice,
  1614.                                           hd3dWorldMatrix,
  1615.                                           &d3dWorldMatrix);
  1616.     if (FAILED(hRes))
  1617.         return hRes;
  1618.  
  1619.     return DD_OK;
  1620. }
  1621.  
  1622. /***********************************************************************/
  1623.  
  1624. /*
  1625.  * Update the viewport in response to a change in window size. This
  1626.  * ensures that we render at a resolution which matches the client
  1627.  * area of the target window.
  1628.  */
  1629. static HRESULT
  1630. UpdateViewport(void)
  1631. {
  1632.     D3DVIEWPORT d3dViewport;
  1633.  
  1634.     ASSERT(NULL != lpd3dViewport);
  1635.  
  1636.     ZeroMemory(&d3dViewport, sizeof(d3dViewport));
  1637.     d3dViewport.dwSize   = sizeof(d3dViewport);
  1638.     d3dViewport.dwX      = 0UL;
  1639.     d3dViewport.dwY      = 0UL;
  1640.     d3dViewport.dwWidth  = (DWORD)rSrcRect.right;
  1641.     d3dViewport.dwHeight = (DWORD)rSrcRect.bottom;
  1642.     d3dViewport.dvScaleX = D3DVAL((float)d3dViewport.dwWidth / 2.0);
  1643.     d3dViewport.dvScaleY = D3DVAL((float)d3dViewport.dwHeight / 2.0);
  1644.     d3dViewport.dvMaxX   = D3DVAL(1.0);
  1645.     d3dViewport.dvMaxY   = D3DVAL(1.0);
  1646.     return lpd3dViewport->lpVtbl->SetViewport(lpd3dViewport, &d3dViewport);
  1647. }
  1648.  
  1649. /***********************************************************************/
  1650.  
  1651. /*
  1652.  * Render the 3D scene.
  1653.  *
  1654.  * Fundamentally this involved submitting our single execute buffer.
  1655.  * However, we also need to clear the back and z-buffers and demark
  1656.  * the start and end of the scene (which in this case is a single
  1657.  * execute).
  1658.  */
  1659. static HRESULT
  1660. RenderScene(void)
  1661. {
  1662.     HRESULT hRes;
  1663.     D3DRECT d3dRect;
  1664.  
  1665.     ASSERT(NULL != lpd3dViewport);
  1666.     ASSERT(NULL != lpd3dDevice);
  1667.     ASSERT(NULL != lpd3dExecuteBuffer);
  1668.  
  1669.     /*
  1670.      * Clear both back and z-buffer.
  1671.      *
  1672.      * NOTE: Its safe to specify the z-buffer clear flag even if we
  1673.      * don't have an attached z-buffer. Direct3D will simply discard
  1674.      * the flag if no z-buffer is being used.
  1675.      *
  1676.      * NOTE: For maximum efficiency we only want to clear those
  1677.      * regions of the device surface and z-buffer which we actually
  1678.      * rendered to in the last frame. This is the purpose of the
  1679.      * array of rectangles and count passed to this function. It is
  1680.      * possible to query Direct3D for the regions of the device
  1681.      * surface that were rendered to by that execute. The application
  1682.      * can then accumulate those rectangles and clear only those
  1683.      * regions. However this is a very simple sample and so, for
  1684.      * simplicity, we will just clear the entire device surface and
  1685.      * z-buffer. Probably not something you want to do in a real
  1686.      * application.
  1687.      */
  1688.     d3dRect.lX1 = rSrcRect.left;
  1689.     d3dRect.lX2 = rSrcRect.right;
  1690.     d3dRect.lY1 = rSrcRect.top;
  1691.     d3dRect.lY2 = rSrcRect.bottom;
  1692.     hRes = lpd3dViewport->lpVtbl->Clear(lpd3dViewport,
  1693.                                         1UL,
  1694.                                         &d3dRect,
  1695.                                         D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER);
  1696.     if (FAILED(hRes))
  1697.         return hRes;
  1698.  
  1699.     /*
  1700.      * Start the scene.
  1701.      *
  1702.      * This function must be called once and once only for every frame
  1703.      * of animation. If you have multiple execute buffers comprising a
  1704.      * single frame you must have one call to BeginScene() before
  1705.      * submitting those execute buffers.
  1706.      *
  1707.      * NOTE: If you have more than one device being rendered in a
  1708.      * single frame, say a rear view mirror in a racing game, call
  1709.      * BeginScene() and EndScene() once for each device.
  1710.      */
  1711.     hRes = lpd3dDevice->lpVtbl->BeginScene(lpd3dDevice);
  1712.     if (FAILED(hRes))
  1713.         return hRes;
  1714.  
  1715.     /*
  1716.      * Submit the execute buffer.
  1717.      *
  1718.      * We want Direct3D to clip the data on our behalf so we specify
  1719.      * D3DEXECUTE_CLIPPED.
  1720.      */
  1721.     hRes = lpd3dDevice->lpVtbl->Execute(lpd3dDevice,
  1722.                                         lpd3dExecuteBuffer,
  1723.                                         lpd3dViewport,
  1724.                                         D3DEXECUTE_CLIPPED);
  1725.     if (FAILED(hRes))
  1726.     {
  1727.         lpd3dDevice->lpVtbl->EndScene(lpd3dDevice);
  1728.         return hRes;
  1729.     }
  1730.  
  1731.     /*
  1732.      * End the scene.
  1733.      */
  1734.     hRes = lpd3dDevice->lpVtbl->EndScene(lpd3dDevice);
  1735.     if (FAILED(hRes))
  1736.         return hRes;
  1737.  
  1738.     /*
  1739.      * At this point the scene will have been rendered and the device
  1740.      * surface will hold the contents of the rendering.
  1741.      */
  1742.  
  1743.     return DD_OK;
  1744. }
  1745.  
  1746. /***********************************************************************/
  1747.  
  1748. /*
  1749.  * Render and show a single frame.
  1750.  *
  1751.  * This involves rendering the scene and blitting the result to client
  1752.  * area of the application window on the primary surface.
  1753.  *
  1754.  * NOTE: This function handles lost surfaces by attempting to restore
  1755.  * the applications surfaces and then retrying the rendering.
  1756.  */
  1757. static HRESULT
  1758. DoFrame(HWND hwnd)
  1759. {
  1760.     HRESULT hRes;
  1761.  
  1762.     /*
  1763.      * We keeping trying until we succeed or we fail for a reason
  1764.      * other than DDERR_SURFACELOST.
  1765.      */
  1766.     while (TRUE)
  1767.     {
  1768.         hRes = RenderScene();
  1769.         if (SUCCEEDED(hRes))
  1770.         {
  1771.         POINT pt;
  1772.         RECT rTmp;
  1773.  
  1774.         pt.x = pt.y = 0;
  1775.         ClientToScreen( hwnd, &pt );
  1776.  
  1777.         rTmp = rDstRect;
  1778.         OffsetRect(&rTmp, pt.x, pt.y);
  1779.  
  1780.             hRes = lpddPrimary->lpVtbl->Blt(lpddPrimary,
  1781.                                             &rTmp,
  1782.                                             lpddDevice,
  1783.                                             &rSrcRect,
  1784.                                             DDBLT_WAIT,
  1785.                                             NULL);
  1786.             if (SUCCEEDED(hRes))
  1787.                 /*
  1788.                  * It worked. Bail.
  1789.                  */
  1790.                 return hRes;
  1791.         }
  1792.         while (DDERR_SURFACELOST == hRes)
  1793.             /*
  1794.              * The surfaces are lost. Restore them.
  1795.              */
  1796.             hRes = RestoreSurfaces();
  1797.         if (FAILED(hRes))
  1798.             /*
  1799.              * Something went wrong and it wasn't DDERR_SURFACELOST.
  1800.              */
  1801.             return hRes;
  1802.     }
  1803. }
  1804.  
  1805. /***********************************************************************/
  1806.  
  1807. /*
  1808.  * The application suspends when in the background or when handling and
  1809.  * error. We signal this fact by drawing a notification string in the
  1810.  * client area of the window.
  1811.  */
  1812. static void
  1813. PaintSuspended(HWND hwnd, HDC hdc)
  1814. {
  1815.     HPEN     hOldPen;
  1816.     HBRUSH   hOldBrush;
  1817.     COLORREF crOldTextColor;
  1818.     int      oldMode;
  1819.     int      x;
  1820.     int      y;
  1821.     SIZE     size;
  1822.     RECT     rect;
  1823.     int      nStrLen;
  1824.  
  1825.     /*
  1826.      * Black background.
  1827.      */
  1828.     hOldPen   = SelectObject(hdc, GetStockObject(NULL_PEN));
  1829.     hOldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));
  1830.  
  1831.     /*
  1832.      * White text.
  1833.      */
  1834.     oldMode = SetBkMode(hdc, TRANSPARENT);
  1835.     crOldTextColor = SetTextColor(hdc, RGB(255, 255, 255));
  1836.  
  1837.     GetClientRect(hwnd, &rect);
  1838.  
  1839.     /*
  1840.      * Clear the client area.
  1841.      */
  1842.     Rectangle(hdc, rect.left, rect.top, rect.right + 1, rect.bottom + 1);
  1843.  
  1844.     /*
  1845.      * Draw the string centered in the client area.
  1846.      */
  1847.     nStrLen = strlen(PAUSED_STRING);
  1848.     GetTextExtentPoint32(hdc, PAUSED_STRING, nStrLen, &size);
  1849.     x = (rect.right  - size.cx) / 2;
  1850.     y = (rect.bottom - size.cy) / 2;
  1851.     TextOut(hdc, x, y, PAUSED_STRING, nStrLen);
  1852.  
  1853.     SetTextColor(hdc, crOldTextColor);
  1854.     SetBkMode(hdc, oldMode);
  1855.  
  1856.     SelectObject(hdc, hOldBrush);
  1857.     SelectObject(hdc, hOldPen);
  1858. }
  1859.  
  1860. /***********************************************************************/
  1861.  
  1862. static LRESULT
  1863. OnMove(HWND hwnd, int x, int y)
  1864. {
  1865.     HRESULT hRes;
  1866.  
  1867.     /*
  1868.      * No action if the device has not yet been created or if we are
  1869.      * suspended.
  1870.      */
  1871.     if ((NULL != lpd3dDevice) && !fSuspended)
  1872.     {
  1873.         /*
  1874.          * Repaint the client area.
  1875.          */
  1876.         hRes = DoFrame(hwnd);
  1877.         if (FAILED(hRes))
  1878.         {
  1879.             FatalError(hwnd, IDS_ERRMSG_RENDERSCENE, hRes);
  1880.             return 0L;
  1881.         }
  1882.     }
  1883.  
  1884.     return 0L;
  1885. }
  1886.  
  1887. /***********************************************************************/
  1888.  
  1889. static LRESULT
  1890. OnSize(HWND hwnd, int w, int h)
  1891. {
  1892.     HRESULT       hRes;
  1893.     DDSURFACEDESC ddsd;
  1894.  
  1895.     /*
  1896.      * Nothing to do if we are suspended.
  1897.      */
  1898.     if (!fSuspended)
  1899.     {
  1900.         /*
  1901.          * Update the source and destination rectangles (used by the
  1902.          * blit which shows the rendering in the client area).
  1903.          */
  1904.         rDstRect.right  = rDstRect.left + w;
  1905.         rDstRect.bottom = rDstRect.top  + h;
  1906.         rSrcRect.right  = w;
  1907.         rSrcRect.bottom = h;
  1908.  
  1909.         if (NULL != lpd3dDevice)
  1910.         {
  1911.             /*
  1912.              * We already have a device. But is it big enough for the the
  1913.              * new window client size?
  1914.              *
  1915.              * NOTE: As this window is fixed size we should not ever be
  1916.              * end up being resized. But just in case we will handle it.
  1917.              * This will be useful when we make the application resizable.
  1918.              */
  1919.             ZeroMemory(&ddsd, sizeof(ddsd));
  1920.             ddsd.dwSize = sizeof(ddsd);
  1921.             hRes = lpddDevice->lpVtbl->GetSurfaceDesc(lpddDevice, &ddsd);
  1922.             if (FAILED(hRes))
  1923.             {
  1924.                 FatalError(hwnd, IDS_ERRMSG_DEVICESIZE, hRes);
  1925.                 return 0L;
  1926.             }
  1927.     
  1928.             if ((w > (int)ddsd.dwWidth) || (h > (int)ddsd.dwHeight))
  1929.             {
  1930.                 /*
  1931.                  * Nope, the device is too small. We need to shut it down
  1932.                  * and rebuild it.
  1933.                  */
  1934.  
  1935.                 /*
  1936.                  * Execute buffers are bound to devices so when we release
  1937.                  * the device we must release the execute buffer.
  1938.                  */
  1939.                 ReleaseScene();
  1940.                 ReleaseDevice();
  1941.             }
  1942.         }
  1943.  
  1944.         if (NULL == lpd3dDevice)
  1945.         {
  1946.             /*
  1947.              * No Direct3D device yet. This is either because this is the
  1948.              * first time through the loop or because we discarded the
  1949.              * existing device because it was not big enough for the new
  1950.              * window client size.
  1951.              */
  1952.             hRes = CreateDevice((DWORD)w, (DWORD)h);
  1953.             if (FAILED(hRes))
  1954.             {
  1955.                 FatalError(hwnd, IDS_ERRMSG_CREATEDEVICE, hRes);
  1956.                 return 0L;
  1957.             }
  1958.             hRes = CreateScene();
  1959.             if (FAILED(hRes))
  1960.             {
  1961.                 FatalError(hwnd, IDS_ERRMSG_BUILDSCENE, hRes);
  1962.                 return 0L;
  1963.             }
  1964.         }
  1965.  
  1966.         hRes = UpdateViewport();
  1967.         if (FAILED(hRes))
  1968.         {
  1969.             FatalError(hwnd, IDS_ERRMSG_UPDATEVIEWPORT, hRes);
  1970.             return 0L;
  1971.         }
  1972.  
  1973.         /*
  1974.          * Render at the new size and show the results in the window's
  1975.          * client area.
  1976.          */
  1977.         hRes = DoFrame(hwnd);
  1978.         if (FAILED(hRes))
  1979.         {
  1980.             FatalError(hwnd, IDS_ERRMSG_RENDERSCENE, hRes);
  1981.             return 0L;
  1982.         }
  1983.     }
  1984.  
  1985.     return 0L;
  1986. }
  1987.  
  1988. /***********************************************************************/
  1989.  
  1990. static LRESULT
  1991. OnPaint(HWND hwnd, HDC hdc, LPPAINTSTRUCT lpps)
  1992. {
  1993.     HRESULT hRes;
  1994.  
  1995.     USE_PARAM(lpps);
  1996.  
  1997.     if (fActive && !fSuspended && (NULL != lpd3dDevice))
  1998.     {
  1999.         /*
  2000.          * NOTE: DoFrame() re-renders the scene as well as blitting the
  2001.          * result to the primary. As all we really want to do here is
  2002.          * repaint the client area we don't really need to re-render -
  2003.          * just re-blit. For this simple sample this inefficiency
  2004.          * doesn't matter but for real applications not re-rendering
  2005.          * may be a useful optimization.
  2006.          */
  2007.         hRes = DoFrame(hwnd);
  2008.         if (FAILED(hRes))
  2009.         {
  2010.             FatalError(hwnd, IDS_ERRMSG_RENDERSCENE, hRes);
  2011.             return 0L;
  2012.         }
  2013.     }
  2014.     else
  2015.     {
  2016.         /*
  2017.          * Show the suspended image if we are not active, or suspended or
  2018.          * if we have not yet created the device.
  2019.          */
  2020.         PaintSuspended(hwnd, hdc);
  2021.     }
  2022.  
  2023.     return 0L;
  2024. }
  2025.  
  2026. /***********************************************************************/
  2027.  
  2028. static LRESULT
  2029. OnIdle(HWND hwnd)
  2030. {
  2031.     HRESULT hRes;
  2032.  
  2033.     /*
  2034.      * Only animate if we are the foreground app, we aren't suspended
  2035.      * and we have completed initialization.
  2036.      */
  2037.     if (fActive && !fSuspended && (NULL != lpd3dDevice))
  2038.     {
  2039.         hRes = AnimateScene();
  2040.         if (FAILED(hRes))
  2041.         {
  2042.             FatalError(hwnd, IDS_ERRMSG_ANIMATESCENE, hRes);
  2043.             return 0L;
  2044.         }
  2045.  
  2046.         hRes = DoFrame(hwnd);
  2047.         if (FAILED(hRes))
  2048.         {
  2049.             FatalError(hwnd, IDS_ERRMSG_RENDERSCENE, hRes);
  2050.             return 0L;
  2051.         }
  2052.     }
  2053.  
  2054.     return 0L;
  2055. }
  2056.  
  2057. /***********************************************************************/
  2058.  
  2059. LRESULT CALLBACK
  2060. WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2061. {
  2062.     HDC         hdc;
  2063.     PAINTSTRUCT ps;
  2064.     LRESULT     lResult;
  2065.     HRESULT     hRes;
  2066.     char        szBuffer[128];
  2067.  
  2068.     switch (msg)
  2069.     {
  2070.         case WM_CREATE:
  2071.             hRes = CreateDirect3D(hwnd);
  2072.             if (FAILED(hRes))
  2073.             {
  2074.                 ReportError(hwnd, IDS_ERRMSG_CREATEDEVICE, hRes);
  2075.                 ReleaseDirect3D();
  2076.                 return -1L;
  2077.             }
  2078.  
  2079.             hRes = CreatePrimary(hwnd);
  2080.             if (FAILED(hRes))
  2081.             {
  2082.                 ReportError(hwnd, IDS_ERRMSG_INITSCREEN, hRes);
  2083.                 ReleasePrimary();
  2084.                 ReleaseDirect3D();
  2085.                 return -1L;
  2086.             }
  2087.  
  2088.             hRes = ChooseDevice();
  2089.             if (FAILED(hRes))
  2090.             {
  2091.                 ReportError(hwnd, IDS_ERRMSG_NODEVICE, hRes);
  2092.                 ReleasePrimary();
  2093.                 ReleaseDirect3D();
  2094.                 return -1L;
  2095.             }
  2096.  
  2097.             /*
  2098.              * Update the title to show the name of the chosen device.
  2099.              */
  2100.             wsprintf(szBuffer, "%s: %s", WINDOW_TITLE, szDeviceName);
  2101.             SetWindowText(hwnd, szBuffer);
  2102.  
  2103.             return 0L;
  2104.  
  2105.         case WM_MOVE:
  2106.             return OnMove(hwnd, (int)(signed short)LOWORD(lParam), (int)(signed short)HIWORD(lParam));
  2107.  
  2108.         case WM_SIZE:
  2109.             return OnSize(hwnd, (int)LOWORD(lParam), (int)HIWORD(lParam));
  2110.  
  2111.         case WM_ERASEBKGND:
  2112.             /*
  2113.              * Our rendering fills the entire viewport so we won't bother
  2114.              * erasing the background.
  2115.              */
  2116.             return 1L;
  2117.  
  2118.         case WM_PAINT:
  2119.             hdc = BeginPaint(hwnd, &ps);
  2120.  
  2121.             lResult = OnPaint(hwnd, hdc, &ps);
  2122.  
  2123.             EndPaint(hwnd, &ps);
  2124.             return lResult;
  2125.  
  2126.         case WM_ACTIVATEAPP:
  2127.             fActive = (BOOL)wParam;
  2128.             if (fActive && !fSuspended && (NULL != lpddPalette))
  2129.             {
  2130.                 /*
  2131.                  * Realizing the palette using DirectDraw is quite different
  2132.                  * from GDI. To realize the palette we call SetPalette()
  2133.                  * each time our application is activated.
  2134.                  *
  2135.                  * NOTE: DirectDraw spots the fact that the new palette is the
  2136.                  * same as the old one and so does not increase the reference
  2137.                  * count of the palette.
  2138.                  */
  2139.                 hRes = lpddPrimary->lpVtbl->SetPalette(lpddPrimary, lpddPalette);
  2140.                 if (FAILED(hRes))
  2141.                 {
  2142.                     FatalError(hwnd, IDS_ERRMSG_REALIZEPALETTE, hRes);
  2143.                     return 0L;
  2144.                 }
  2145.  
  2146.             }
  2147.             else
  2148.             {
  2149.                 /*
  2150.                  * If we have been deactived invalidate to show the suspended
  2151.                  * display.
  2152.                  */
  2153.                 InvalidateRect(hwnd, NULL, FALSE);
  2154.             }
  2155.             return 0L;
  2156.  
  2157.         case WM_KEYUP:
  2158.             /*
  2159.              * We use the escape key as a quick way of getting out of the
  2160.              * application.
  2161.              */
  2162.             if (VK_ESCAPE == (int)wParam)
  2163.             {
  2164.                 DestroyWindow(hwnd);
  2165.                 return 0L;
  2166.             }
  2167.             break;
  2168.  
  2169.         case WM_CLOSE:
  2170.             DestroyWindow(hwnd);
  2171.             return 0L;
  2172.  
  2173.         case WM_DESTROY:
  2174.             /*
  2175.              * All cleanup is done here when terminating normally or
  2176.              * shutting down due to an error.
  2177.              */
  2178.             ReleaseScene();
  2179.             ReleaseDevice();
  2180.             ReleasePrimary();
  2181.             ReleaseDirect3D();
  2182.  
  2183.             PostQuitMessage(0);
  2184.             return 0L;
  2185.     }
  2186.  
  2187.     return DefWindowProc(hwnd, msg, wParam, lParam);
  2188. }
  2189.                         
  2190. /***********************************************************************/
  2191.  
  2192. int PASCAL
  2193. WinMain(HINSTANCE hInstance,
  2194.         HINSTANCE hPrevInstance,
  2195.         LPSTR     lpszCommandLine,
  2196.         int       cmdShow)
  2197. {
  2198.     WNDCLASS wndClass;
  2199.     HWND     hwnd;
  2200.     MSG      msg;
  2201.  
  2202.     USE_PARAM(hPrevInstance);
  2203.  
  2204.     /*
  2205.      * Record the instance handle.
  2206.      */
  2207.     hAppInstance = hInstance;
  2208.  
  2209.     /*
  2210.      * Very, very primitive command line processing. We only have one
  2211.      * option, debug so we will just assume that if anything was
  2212.      * specified on the command line that means debug mode (no hardware
  2213.      * all surfaces explicitly in system memory).
  2214.      */
  2215.     if (0 != *lpszCommandLine)
  2216.         fDebug = TRUE;
  2217.  
  2218.     /*
  2219.      * Register the window class.
  2220.      */
  2221.     wndClass.style         = 0;
  2222.     wndClass.lpfnWndProc   = WndProc;
  2223.     wndClass.cbClsExtra    = 0;
  2224.     wndClass.cbWndExtra    = 0;
  2225.     wndClass.hInstance     = hInstance;
  2226.     wndClass.hIcon         = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_APPICON));
  2227.     wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  2228.     wndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
  2229.     wndClass.lpszMenuName  = NULL;
  2230.     wndClass.lpszClassName = WINDOW_CLASSNAME;
  2231.  
  2232.     RegisterClass(&wndClass);
  2233.  
  2234.     /*
  2235.      * Create the main window of the instance.
  2236.      */
  2237.     hwnd = CreateWindow(WINDOW_CLASSNAME,
  2238.                         WINDOW_TITLE,
  2239.                         WS_OVERLAPPED | WS_SYSMENU,
  2240.                         CW_USEDEFAULT, CW_USEDEFAULT,
  2241.                         WINDOW_WIDTH, WINDOW_HEIGHT,
  2242.                         NULL,
  2243.                         NULL,
  2244.                         hInstance,
  2245.                         NULL);
  2246.  
  2247.     ShowWindow(hwnd, cmdShow);
  2248.     UpdateWindow(hwnd);
  2249.  
  2250.     /*
  2251.      * The main message dispatch loop.
  2252.      *
  2253.      * NOTE: For simplicity we handle the message loop with a
  2254.      * simple PeekMessage scheme. This might not be the best
  2255.      * mechanism for a real application (a separate render worker
  2256.      * thread might be better). 
  2257.      */
  2258.     while (TRUE)
  2259.     {
  2260.         if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
  2261.         {
  2262.             /*
  2263.              * Message pending. If its QUIT then exit the message
  2264.              * loop. Otherwise, process the message.
  2265.              */
  2266.             if (WM_QUIT == msg.message)
  2267.             {
  2268.                 break;
  2269.             }
  2270.             else
  2271.             {
  2272.                 TranslateMessage(&msg);
  2273.                 DispatchMessage(&msg);
  2274.             }
  2275.         }
  2276.         else
  2277.         {
  2278.             /*
  2279.              * Animate the scene.
  2280.              */
  2281.             OnIdle(hwnd);
  2282.         }
  2283.     }
  2284.  
  2285.     return msg.wParam;
  2286. }
  2287.  
  2288. /***********************************************************************/
  2289.  
  2290. /***********************************************************************
  2291.  *
  2292.  * End of file
  2293.  *
  2294.  ***********************************************************************/
  2295.