D3DEX1.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:70k
源码类别:

Windows编程

开发平台:

Visual C++

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