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 / misc / ddcalls.c < prev    next >
C/C++ Source or Header  |  1997-07-14  |  34KB  |  1,001 lines

  1. /*
  2.  *  Copyright (C) 1995, 1996 Microsoft Corporation. All Rights Reserved.
  3.  *
  4.  *  File: ddcalls.c
  5.  *
  6.  *  Manages DirectDraw objects needed for rendering.  Part of D3DApp.
  7.  *
  8.  *  D3DApp is a collection of helper functions for Direct3D applications.
  9.  *  D3DApp consists of the following files:
  10.  *    d3dapp.h    Main D3DApp header to be included by application
  11.  *      d3dappi.h   Internal header
  12.  *      d3dapp.c    D3DApp functions seen by application.
  13.  *      ddcalls.c   All calls to DirectDraw objects except textures
  14.  *      d3dcalls.c  All calls to Direct3D objects except textures
  15.  *      texture.c   Texture loading and managing texture list
  16.  *      misc.c        Miscellaneous calls
  17.  */
  18.  
  19. #include "d3dappi.h"
  20.  
  21. /***************************************************************************/
  22. /*                         Direct Draw Enumeration                         */
  23. /***************************************************************************/
  24.  
  25. typedef struct {
  26.     D3DAppDDDriver* driver;
  27.     int            numdriver;
  28. } DDEnumCallbackContext;
  29.  
  30. /*
  31.  * D3DAppIDDEnumCallback
  32.  * Callback function used during enumeration of DirectDraw drivers.
  33.  */
  34. BOOL FAR PASCAL D3DAppIDDEnumCallback(GUID FAR* lpGUID, LPSTR lpDriverDesc, LPSTR lpDriverName, LPVOID lpContext)
  35. {
  36.     LPDIRECTDRAW lpDD;
  37.     DDCAPS DriverCaps, HELCaps;
  38.     DDEnumCallbackContext *context = (DDEnumCallbackContext*)lpContext;
  39.     D3DAppDDDriver* driver = &context->driver[context->numdriver];
  40.  
  41.     /*
  42.      * Create the DirectDraw device using this driver.  If it fails,
  43.      * just move on to the next driver.
  44.      */
  45.     if (FAILED(DirectDrawCreate(lpGUID, &lpDD, NULL))) {
  46.     return DDENUMRET_OK;
  47.     }
  48.     /*
  49.      * Get the capabilities of this DirectDraw driver.  If it fails,
  50.      * just move on to the next driver.
  51.      */
  52.     memset(&DriverCaps, 0, sizeof(DDCAPS));
  53.     DriverCaps.dwSize = sizeof(DDCAPS);
  54.     memset(&HELCaps, 0, sizeof(DDCAPS));
  55.     HELCaps.dwSize = sizeof(DDCAPS);
  56.     if (FAILED(lpDD->lpVtbl->GetCaps(lpDD, &DriverCaps, &HELCaps))) {
  57.     lpDD->lpVtbl->Release(lpDD);
  58.     return DDENUMRET_OK;
  59.     }
  60.     if (lpGUID && (DriverCaps.dwCaps & DDCAPS_3D)) {
  61.     /*
  62.      * We have found a secondary 3d hardware device.  Return the DD object
  63.      * and stop enumeration.
  64.      */
  65.     memcpy(&driver->Guid, lpGUID, sizeof(GUID));
  66.     memcpy(&driver->HWCaps, &DriverCaps, sizeof(DDCAPS));
  67.     lstrcpy(driver->Name, lpDriverName);
  68.     driver->bIsPrimary = FALSE;
  69.     context->numdriver++;
  70.     } else {
  71.     /*
  72.      * It's the primary, fill in some fields.
  73.      */
  74.     memset(&driver->Guid, 0, sizeof(GUID));
  75.     memcpy(&driver->HWCaps, &DriverCaps, sizeof(DDCAPS));
  76.     lstrcpy(driver->Name, "Primary Device");
  77.     driver->bIsPrimary = TRUE;
  78.     context->numdriver++;
  79.     }
  80.     lpDD->lpVtbl->Release(lpDD);
  81.     if (context->numdriver >= D3DAPP_MAXDDDRIVERS)
  82.     return DDENUMRET_CANCEL;
  83.     else
  84.         return DDENUMRET_OK;
  85. }
  86.  
  87. /*
  88.  * D3DAppIEnumDDDevices
  89.  * Enumerate all 3D capable devices and the primary display, filling in
  90.  * the driver information variables.  lpDriver must point to a region
  91.  * with enough room for the maximum DD drivers.
  92.  */
  93. BOOL
  94. D3DAppIEnumDDDevices(int* NumDriver, D3DAppDDDriver* lpDriver)
  95. {
  96.     DDEnumCallbackContext context;
  97.  
  98.     context.numdriver = 0;
  99.     context.driver = lpDriver;
  100.     LastError = DirectDrawEnumerate(D3DAppIDDEnumCallback, &context);
  101.     if (LastError != DD_OK) {
  102.     D3DAppISetErrorString("DirectDrawEnumerate failed.\n%s",
  103.                   D3DAppErrorToString(LastError));
  104.     return FALSE;
  105.     }
  106.     *NumDriver = context.numdriver;
  107.     return TRUE;
  108. }
  109.  
  110. /***************************************************************************/
  111. /*                         Direct Draw Creation                            */
  112. /***************************************************************************/
  113.  
  114. /*
  115.  * D3DAppICreateDD
  116.  * Creates the DirectDraw device and saves the current palette.
  117.  * It is assumed that a 3D capable secondary DD driver is not the
  118.  * primary device and hence cannot operate in a window (ie it's a
  119.  * fullscreen only device displaying on a second monitor).  Valid flags:
  120.  *     D3DAPP_ONLYDDEMULATION    Always use the DirectDraw HEL
  121.  */
  122. BOOL
  123. D3DAppICreateDD(DWORD flags, LPGUID lpGuid)
  124. {
  125.     HDC hdc;
  126.     int i;
  127.     LPDIRECTDRAW lpDD = NULL;
  128.  
  129.     if (flags & D3DAPP_ONLYDDEMULATION)
  130.     lpGuid = NULL;
  131.  
  132.     if (!lpGuid)
  133.     d3dappi.bIsPrimary = TRUE;
  134.     else
  135.     d3dappi.bIsPrimary = FALSE;
  136.  
  137.     LastError = DirectDrawCreate(lpGuid, &d3dappi.lpDD, NULL);
  138.     if (LastError != DD_OK) {
  139.     D3DAppISetErrorString("DirectDrawCreate failed.\n%s",
  140.                   D3DAppErrorToString(LastError));
  141.     return FALSE;
  142.     }
  143.     /*
  144.      * Save the original palette for when we are paused.  Just in case we
  145.      * start in a fullscreen mode, put them in ppe.
  146.      */
  147.     hdc = GetDC(NULL);
  148.     GetSystemPaletteEntries(hdc, 0, (1 << 8),
  149.                 (LPPALETTEENTRY)(&Originalppe[0]));
  150.     for (i = 0; i < 256; i++)
  151.         ppe[i] = Originalppe[i];
  152.     ReleaseDC(NULL, hdc);
  153.     return TRUE;
  154. }
  155.  
  156. /***************************************************************************/
  157. /*                   Enumerating the display modes                         */
  158. /***************************************************************************/
  159. /*
  160.  * EnumDisplayModesCallback
  161.  * Callback to save the display mode information.
  162.  */
  163. static HRESULT
  164. CALLBACK EnumDisplayModesCallback(LPDDSURFACEDESC pddsd, LPVOID lpContext)
  165. {
  166.     /*
  167.      * Very large resolutions cause problems on some hardware.  They are also
  168.      * not very useful for real-time rendering.  We have chosen to disable
  169.      * them by not reporting them as available.
  170.      */
  171.     if (pddsd->dwWidth > 1024 || pddsd->dwHeight > 768)
  172.     return DDENUMRET_OK;
  173.     /*
  174.      * Save this mode at the end of the mode array and increment mode count
  175.      */
  176.     d3dappi.Mode[d3dappi.NumModes].w = pddsd->dwWidth;
  177.     d3dappi.Mode[d3dappi.NumModes].h = pddsd->dwHeight;
  178.     d3dappi.Mode[d3dappi.NumModes].bpp = pddsd->ddpfPixelFormat.dwRGBBitCount;
  179.     d3dappi.Mode[d3dappi.NumModes].bThisDriverCanDo = FALSE;
  180.     d3dappi.NumModes++;
  181.     if (d3dappi.NumModes == D3DAPP_MAXMODES)
  182.     return DDENUMRET_CANCEL;
  183.     else
  184.     return DDENUMRET_OK;
  185. }
  186.  
  187. /*
  188.  * CompareModes
  189.  * Compare two display modes during sorting.  Modes are sorted by depth and
  190.  * then resolution.
  191.  */
  192. static int
  193. _cdecl CompareModes(const void* element1, const void* element2) {
  194.     D3DAppMode *lpMode1, *lpMode2;
  195.  
  196.     lpMode1 = (D3DAppMode*)element1;
  197.     lpMode2 = (D3DAppMode*)element2;
  198.  
  199.     if (lpMode1->bpp > lpMode2->bpp)
  200.         return -1;
  201.     else if (lpMode2->bpp > lpMode1->bpp)
  202.         return 1;
  203.     else if (lpMode1->w > lpMode2->w)
  204.         return -1;
  205.     else if (lpMode2->w > lpMode1->w)
  206.         return 1;
  207.     else if (lpMode1->h > lpMode2->h)
  208.         return -1;
  209.     else if (lpMode2->h > lpMode1->h)
  210.         return 1;
  211.     else
  212.         return 0;
  213. }
  214.  
  215. /*
  216.  * EnumerateDisplayModes
  217.  * Generates the list of available display modes.
  218.  */
  219. BOOL
  220. D3DAppIEnumDisplayModes(void)
  221. {
  222.     int i;
  223.     /*
  224.      * Get a list of available display modes from DirectDraw
  225.      */
  226.     d3dappi.NumModes = 0;
  227.     LastError = d3dappi.lpDD->lpVtbl->EnumDisplayModes(d3dappi.lpDD, 0, NULL,
  228.                         0, EnumDisplayModesCallback);
  229.     if(LastError != DD_OK ) {
  230.         D3DAppISetErrorString("EnumDisplayModes failed.\n%s",
  231.                   D3DAppErrorToString(LastError));
  232.     d3dappi.NumModes = 0;
  233.         return FALSE;
  234.     }
  235.     /*
  236.      * Sort the list of display modes
  237.      */
  238.     qsort((void *)&d3dappi.Mode[0], (size_t)d3dappi.NumModes, sizeof(D3DAppMode),
  239.           CompareModes);
  240.     /*
  241.      * Pick a default display mode.  640x480x16 is a very good mode for
  242.      * rendering, so choose it over all others.  Otherwise, just take the
  243.      * first one.  This selection may be overriden later if a driver is
  244.      * created which cannot render in this mode.
  245.      */
  246.     d3dappi.CurrMode = 0;
  247.     for (i = 0; i < d3dappi.NumModes; i++) {
  248.     if (d3dappi.Mode[i].w == 640 && d3dappi.Mode[i].h == 480 &&
  249.         d3dappi.Mode[i].bpp == 16)
  250.         d3dappi.CurrMode = i;
  251.     }
  252.     memcpy(&d3dappi.ThisMode, &d3dappi.Mode[d3dappi.CurrMode],
  253.        sizeof(D3DAppMode));
  254.     return TRUE;
  255. }
  256.  
  257. /***************************************************************************/
  258. /*               Creating Front and Back Buffers (and misc surf funcs)     */
  259. /***************************************************************************/
  260. /*
  261.  * D3DAppICreateSurface
  262.  * Create a DirectDraw Surface of the given description.  Using this function
  263.  * ensures that all surfaces end up in system memory if that option was set.
  264.  * Returns the result of the CreateSurface call.
  265.  */
  266. HRESULT
  267. D3DAppICreateSurface(LPDDSURFACEDESC lpDDSurfDesc,
  268.                 LPDIRECTDRAWSURFACE FAR *lpDDSurface) {
  269.     HRESULT result;
  270.     if (d3dappi.bOnlySystemMemory)
  271.         lpDDSurfDesc->ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  272.     result = d3dappi.lpDD->lpVtbl->CreateSurface(d3dappi.lpDD, lpDDSurfDesc,
  273.                          lpDDSurface, NULL);
  274.     return result;
  275. }
  276.  
  277. /*
  278.  * D3DAppIGetSurfDesc
  279.  * Get the description of the given surface.  Returns the result of the
  280.  * GetSurfaceDesc call.
  281.  */
  282. HRESULT
  283. D3DAppIGetSurfDesc(LPDDSURFACEDESC lpDDSurfDesc,LPDIRECTDRAWSURFACE lpDDSurf)
  284. {
  285.     HRESULT result;
  286.     memset(lpDDSurfDesc, 0, sizeof(DDSURFACEDESC));
  287.     lpDDSurfDesc->dwSize = sizeof(DDSURFACEDESC);
  288.     result = lpDDSurf->lpVtbl->GetSurfaceDesc(lpDDSurf, lpDDSurfDesc);
  289.     return result;
  290. }
  291.  
  292. /*
  293.  * D3DAppICreateBuffers
  294.  * Creates the front and back buffers for the window or fullscreen case
  295.  * depending on the bFullscreen flag.  In the window case, bpp is ignored.
  296.  */
  297. BOOL
  298. D3DAppICreateBuffers(HWND hwnd, int w, int h, int bpp, BOOL bFullscreen, BOOL bIsHardware)
  299. {
  300.     DDSURFACEDESC ddsd;
  301.     DDSCAPS ddscaps;
  302.  
  303.     /*
  304.      * Release any old objects that might be lying around.  This should have
  305.      * already been taken care of, but just in case...
  306.      */
  307.     RELEASE(lpClipper);
  308.     RELEASE(d3dappi.lpBackBuffer);
  309.     RELEASE(d3dappi.lpFrontBuffer);
  310.     /*
  311.      * The size of the buffers is going to be w x h, so record it now
  312.      */
  313.     if (w < D3DAPP_WINDOWMINIMUM)
  314.     w = D3DAPP_WINDOWMINIMUM;
  315.     if (h < D3DAPP_WINDOWMINIMUM)
  316.     h = D3DAPP_WINDOWMINIMUM;
  317.     szBuffers.cx = w;
  318.     szBuffers.cy = h;
  319.  
  320.     if (bFullscreen) {
  321.         /*
  322.          * Create a complex flipping surface for fullscreen mode with one
  323.      * back buffer.
  324.          */
  325.         memset(&ddsd,0,sizeof(DDSURFACEDESC));
  326.     ddsd.dwSize = sizeof( ddsd );
  327.         ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  328.         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |
  329.             DDSCAPS_3DDEVICE | DDSCAPS_COMPLEX;
  330.         ddsd.dwBackBufferCount = 1;
  331.     if (bIsHardware)
  332.         ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
  333.         LastError = D3DAppICreateSurface(&ddsd, &d3dappi.lpFrontBuffer);
  334.         if(LastError != DD_OK) {
  335.         if (LastError == DDERR_OUTOFMEMORY || LastError == DDERR_OUTOFVIDEOMEMORY) {
  336.         D3DAppISetErrorString("There was not enough video memory to create the rendering surface.\nPlease restart the program and try another fullscreen mode with less resolution or lower bit depth.");
  337.         } else {
  338.         D3DAppISetErrorString("CreateSurface for fullscreen flipping surface failed.\n%s",
  339.                       D3DAppErrorToString(LastError));
  340.         }
  341.             goto exit_with_error;
  342.     }
  343.     /* 
  344.      * Obtain a pointer to the back buffer surface created above so we
  345.      * can use it later.  For now, just check to see if it ended up in
  346.      * video memory (FYI).
  347.      */
  348.         ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
  349.         LastError = d3dappi.lpFrontBuffer->lpVtbl->GetAttachedSurface(d3dappi.lpFrontBuffer, &ddscaps, &d3dappi.lpBackBuffer);
  350.         if(LastError != DD_OK) {
  351.             D3DAppISetErrorString("GetAttachedSurface failed to get back buffer.\n%s",
  352.                   D3DAppErrorToString(LastError));
  353.             goto exit_with_error;
  354.     }
  355.         LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpBackBuffer);
  356.     if (LastError != DD_OK) {
  357.         D3DAppISetErrorString("Failed to get surface description of back buffer.\n%s",
  358.                   D3DAppErrorToString(LastError));
  359.             goto exit_with_error;
  360.     }
  361.         d3dappi.bBackBufferInVideo =
  362.               (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) ? TRUE : FALSE;
  363.     }
  364.     else {
  365.         /*
  366.          * In the window case, create a front buffer which is the primary
  367.      * surface and a back buffer which is an offscreen plane surface.
  368.          */
  369.         memset(&ddsd,0,sizeof(DDSURFACEDESC));
  370.         ddsd.dwSize = sizeof(DDSURFACEDESC);
  371.         ddsd.dwFlags = DDSD_CAPS;
  372.         ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  373.     /*
  374.      * If we specify system memory when creating a primary surface, we
  375.      * won't get the actual primary surface in video memory.  So, don't
  376.      * use D3DAppICreateSurface().
  377.      */
  378.     LastError = d3dappi.lpDD->lpVtbl->CreateSurface(d3dappi.lpDD,
  379.                     &ddsd, &d3dappi.lpFrontBuffer, NULL);
  380.         if(LastError != DD_OK ) {
  381.         if (LastError == DDERR_OUTOFMEMORY || LastError == DDERR_OUTOFVIDEOMEMORY) {
  382.         D3DAppISetErrorString("There was not enough video memory to create the rendering surface.\nTo run this program in a window of this size, please adjust your display settings for a smaller desktop area or a lower palette size and restart the program.");
  383.         } else {
  384.         D3DAppISetErrorString("CreateSurface for window front buffer failed.\n%s",
  385.                       D3DAppErrorToString(LastError));
  386.         }
  387.             goto exit_with_error;
  388.         }
  389.         ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
  390.         ddsd.dwWidth = w;
  391.         ddsd.dwHeight = h;
  392.         ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
  393.     if (bIsHardware)
  394.         ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
  395.     else
  396.         ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  397.         LastError = D3DAppICreateSurface(&ddsd, &d3dappi.lpBackBuffer);
  398.         if (LastError != DD_OK) {
  399.         if (LastError == DDERR_OUTOFMEMORY || LastError == DDERR_OUTOFVIDEOMEMORY) {
  400.         D3DAppISetErrorString("There was not enough video memory to create the rendering surface.\nTo run this program in a window of this size, please adjust your display settings for a smaller desktop area or a lower palette size and restart the program.");
  401.         } else {
  402.         D3DAppISetErrorString("CreateSurface for window back buffer failed.\n%s",
  403.                       D3DAppErrorToString(LastError));
  404.         }
  405.             goto exit_with_error;
  406.     }
  407.     /*
  408.      * Check to see if the back buffer is in video memory (FYI).
  409.      */
  410.     LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpBackBuffer);
  411.     if (LastError != DD_OK) {
  412.         D3DAppISetErrorString("Failed to get surface description for back buffer.\n%s",
  413.                   D3DAppErrorToString(LastError));
  414.             goto exit_with_error;
  415.     }
  416.     d3dappi.bBackBufferInVideo =
  417.               (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) ? TRUE : FALSE;
  418.         /*
  419.          * Create the DirectDraw Clipper object and attach it to the window
  420.      * and front buffer.
  421.          */
  422.         LastError = d3dappi.lpDD->lpVtbl->CreateClipper(d3dappi.lpDD, 0,
  423.                             &lpClipper, NULL);
  424.         if(LastError != DD_OK ) {
  425.             D3DAppISetErrorString("CreateClipper failed.\n%s",
  426.                   D3DAppErrorToString(LastError));
  427.             goto exit_with_error;
  428.     }
  429.         LastError = lpClipper->lpVtbl->SetHWnd(lpClipper, 0, hwnd);
  430.         if(LastError != DD_OK ) {
  431.             D3DAppISetErrorString("Attaching clipper to window failed.\n%s",
  432.                   D3DAppErrorToString(LastError));
  433.             goto exit_with_error;
  434.     }
  435.         LastError =
  436.          d3dappi.lpFrontBuffer->lpVtbl->SetClipper(d3dappi.lpFrontBuffer,
  437.                                lpClipper);
  438.         if(LastError != DD_OK ) {
  439.             D3DAppISetErrorString("Attaching clipper to front buffer failed.\n%s",
  440.                   D3DAppErrorToString(LastError));
  441.             goto exit_with_error;
  442.     }
  443.     }
  444.  
  445.     D3DAppIClearBuffers();
  446.     return TRUE;
  447.  
  448. exit_with_error:
  449.     RELEASE(d3dappi.lpFrontBuffer);
  450.     RELEASE(d3dappi.lpBackBuffer);
  451.     RELEASE(lpClipper);
  452.     return FALSE;
  453. }
  454.  
  455. /*
  456.  * D3DAppICheckForPalettized
  457.  * If the front/back buffer is palettized, we need to create a palette.
  458.  */
  459. BOOL
  460. D3DAppICheckForPalettized(void)
  461. {
  462.     DDSURFACEDESC ddsd;
  463.     /*
  464.      * Get the back buffer surface description and check to see if it's
  465.      * palettized
  466.      */
  467.     LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpBackBuffer);
  468.     if (LastError != DD_OK) {
  469.     D3DAppISetErrorString("Failed to get surface description for back buffer for palettizing.\n%s",
  470.                   D3DAppErrorToString(LastError));
  471.         goto exit_with_error;
  472.     }
  473.     bPrimaryPalettized = 
  474.     (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) ? TRUE : FALSE;
  475.  
  476.     if (bPrimaryPalettized) {
  477.     int i;
  478.     /*
  479.      * Get the current palette.
  480.      */
  481.      HDC hdc = GetDC(NULL);
  482.     GetSystemPaletteEntries(hdc, 0, (1 << 8), ppe);
  483.     ReleaseDC(NULL, hdc);
  484.     /*
  485.      * Change the flags on the palette entries to allow D3D to change
  486.      * some of them.  In the window case, we must not change the top and
  487.      * bottom ten (system colors), but in a fullscreen mode we can have
  488.      * all but the first and last.
  489.      */
  490.         if (!d3dappi.bFullscreen) {
  491.         for (i = 0; i < 10; i++) ppe[i].peFlags = D3DPAL_READONLY;
  492.         for (i = 10; i < 256 - 10; i++) ppe[i].peFlags = D3DPAL_FREE | PC_RESERVED;
  493.         for (i = 256 - 10; i < 256; i++) ppe[i].peFlags = D3DPAL_READONLY;
  494.         } else {
  495.         ppe[0].peFlags = D3DPAL_READONLY;
  496.         for (i = 1; i < 255; i++) ppe[i].peFlags = D3DPAL_FREE | PC_RESERVED;
  497.         ppe[255].peFlags = D3DPAL_READONLY;
  498.     }
  499.     /*
  500.      * Create a palette using the old colors and new flags
  501.      */
  502.     LastError = d3dappi.lpDD->lpVtbl->CreatePalette(d3dappi.lpDD,
  503.                        DDPCAPS_8BIT | DDPCAPS_INITIALIZE,
  504.                        ppe, &lpPalette, NULL);
  505.     if (LastError != DD_OK) {
  506.         D3DAppISetErrorString("CreatePalette failed.\n%s",
  507.                   D3DAppErrorToString(LastError));
  508.             goto exit_with_error;
  509.     }
  510.     /*
  511.      * Set this as the front and back buffers' palette
  512.      */
  513.     LastError =
  514.            d3dappi.lpBackBuffer->lpVtbl->SetPalette(d3dappi.lpBackBuffer,
  515.                                 lpPalette);
  516.         if(LastError != DD_OK ) {
  517.             D3DAppISetErrorString("SetPalette failed on back buffer.\n%s",
  518.                   D3DAppErrorToString(LastError));
  519.             goto exit_with_error;
  520.     }
  521.     LastError =
  522.          d3dappi.lpFrontBuffer->lpVtbl->SetPalette(d3dappi.lpFrontBuffer,
  523.                                lpPalette);
  524.         if(LastError != DD_OK ) {
  525.             D3DAppISetErrorString("SetPalette failed on front buffer.\n%s",
  526.                   D3DAppErrorToString(LastError));
  527.             goto exit_with_error;
  528.     }
  529.     /*
  530.      * The palette is now valid, so set it again on anyt WM_ACTIVATE
  531.      */
  532.     bPaletteActivate = TRUE;
  533.     }
  534.     return TRUE;
  535. exit_with_error:
  536.     RELEASE(lpPalette);
  537.     return FALSE;
  538. }
  539.  
  540. /***************************************************************************/
  541. /*                           Creation of Z-Buffer                          */
  542. /***************************************************************************/
  543. /*
  544.  * D3DAppICreateZBuffer
  545.  * Create a Z-Buffer of the appropriate depth and attach it to the back
  546.  * buffer.
  547.  */
  548. BOOL
  549. D3DAppICreateZBuffer(int w, int h, int driver)
  550. {
  551.     DDSURFACEDESC ddsd;
  552.     DWORD devDepth;
  553.     /*
  554.      * Release any Z-Buffer that might be around just in case.
  555.      */
  556.     RELEASE(d3dappi.lpZBuffer);
  557.     
  558.     /*
  559.      * If this driver does not do z-buffering, don't create a z-buffer
  560.      */
  561.     if (!d3dappi.Driver[driver].bDoesZBuffer)
  562.     return TRUE;
  563.  
  564.     memset(&ddsd, 0 ,sizeof(DDSURFACEDESC));
  565.     ddsd.dwSize = sizeof( ddsd );
  566.     ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS |
  567.            DDSD_ZBUFFERBITDEPTH;
  568.     ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER;
  569.     ddsd.dwHeight = h;
  570.     ddsd.dwWidth = w;
  571.     /*
  572.      * If this is a hardware D3D driver, the Z-Buffer MUST end up in video
  573.      * memory.  Otherwise, it MUST end up in system memory.
  574.      */
  575.     if (d3dappi.Driver[driver].bIsHardware)
  576.     ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
  577.     else
  578.     ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
  579.     /*
  580.      * Get the Z buffer bit depth from this driver's D3D device description
  581.      */
  582.     devDepth = d3dappi.Driver[driver].Desc.dwDeviceZBufferBitDepth;
  583.     if (devDepth & DDBD_32)
  584.     ddsd.dwZBufferBitDepth = 32;
  585.     else if (devDepth & DDBD_24)
  586.     ddsd.dwZBufferBitDepth = 24;
  587.     else if (devDepth & DDBD_16)
  588.     ddsd.dwZBufferBitDepth = 16;
  589.     else if (devDepth & DDBD_8)
  590.     ddsd.dwZBufferBitDepth = 8;
  591.     else {
  592.     D3DAppISetErrorString("Unsupported Z-buffer depth requested by device.\n");
  593.     return FALSE;
  594.     }
  595.     LastError = d3dappi.lpDD->lpVtbl->CreateSurface(d3dappi.lpDD, &ddsd,
  596.                             &d3dappi.lpZBuffer,
  597.                             NULL);
  598.     if(LastError != DD_OK) {
  599.     if (LastError == DDERR_OUTOFMEMORY || LastError == DDERR_OUTOFVIDEOMEMORY) {
  600.         if (d3dappi.bFullscreen) {
  601.         D3DAppISetErrorString("There was not enough video memory to create the Z-buffer surface.\nPlease restart the program and try another fullscreen mode with less resolution or lower bit depth.");
  602.         } else {
  603.         D3DAppISetErrorString("There was not enough video memory to create the Z-buffer surface.\nTo run this program in a window of this size, please adjust your display settings for a smaller desktop area or a lower palette size and restart the program.");
  604.         }
  605.     } else {
  606.         D3DAppISetErrorString("CreateSurface for Z-buffer failed.\n%s",
  607.                   D3DAppErrorToString(LastError));
  608.     }
  609.         goto exit_with_error;
  610.     }
  611.     /*
  612.      * Attach the Z-buffer to the back buffer so D3D will find it
  613.      */
  614.     LastError =
  615.        d3dappi.lpBackBuffer->lpVtbl->AddAttachedSurface(d3dappi.lpBackBuffer,
  616.                             d3dappi.lpZBuffer);
  617.     if(LastError != DD_OK) {
  618.         D3DAppISetErrorString("AddAttachedBuffer failed for Z-Buffer.\n%s",
  619.                   D3DAppErrorToString(LastError));
  620.     goto exit_with_error;
  621.     }
  622.     /*
  623.      * Find out if it ended up in video memory.
  624.      */
  625.     LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpZBuffer);
  626.     if (LastError != DD_OK) {
  627.     D3DAppISetErrorString("Failed to get surface description of Z buffer.\n%s",
  628.                   D3DAppErrorToString(LastError));
  629.         goto exit_with_error;
  630.     }
  631.     d3dappi.bZBufferInVideo =
  632.               (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) ? TRUE : FALSE;
  633.     if (d3dappi.Driver[driver].bIsHardware && !d3dappi.bZBufferInVideo) {
  634.     D3DAppISetErrorString("Could not fit the Z-buffer in video memory for this hardware device.\n");
  635.     goto exit_with_error;
  636.     }
  637.  
  638.     return TRUE;
  639.  
  640. exit_with_error:
  641.     RELEASE(d3dappi.lpZBuffer);
  642.     return FALSE;
  643. }
  644.  
  645. /***************************************************************************/
  646. /*                             WM_SIZE Handler                             */
  647. /***************************************************************************/
  648. /*
  649.  * D3DAppIHandleWM_SIZE
  650.  * Processes the WM_SIZE message.  Resizes all the buffers and re-creates
  651.  * device if necessary.
  652.  */
  653. BOOL
  654. D3DAppIHandleWM_SIZE(LRESULT* lresult, HWND hwnd, UINT message,
  655.              WPARAM wParam, LPARAM lParam)
  656. {
  657.     int w, h, i;
  658.     /*
  659.      * If we have minimzied, take note and call the default window proc
  660.      */
  661.     if (wParam == SIZE_MINIMIZED) {
  662.     d3dappi.bMinimized = TRUE;
  663.     *lresult = DefWindowProc(hwnd, message, wParam, lParam);
  664.     return TRUE;
  665.     }
  666.     /*
  667.      * In fullscreen mode, restore our surfaces and let DDraw take
  668.      * care of the rest.
  669.      */
  670.     if (d3dappi.bFullscreen) {
  671.     D3DAppIValidateDirtyRects();
  672.     D3DAppCheckForLostSurfaces();
  673.     d3dappi.bMinimized = FALSE;
  674.     *lresult = DefWindowProc(hwnd, message, wParam, lParam);
  675.     return TRUE;
  676.     }
  677.     /*
  678.      * If we are minimized, this is the un-minimized size message.
  679.      */
  680.     if (d3dappi.bMinimized) {
  681.     /*
  682.      * Restore our surfaces and update the dirty rectangle info
  683.      */
  684.     D3DAppIValidateDirtyRects();
  685.     D3DAppCheckForLostSurfaces();
  686.     d3dappi.bMinimized = FALSE;
  687.     *lresult = DefWindowProc(hwnd, message, wParam, lParam);
  688.     return TRUE;
  689.     }
  690.     /*
  691.      * Since we are still here, this must be a regular, window resize
  692.      * message.  A new viewport will definitely be needed, but the
  693.      * device and buffers will only be re-created if they have gotten bigger
  694.      * or change size by a very large amount.
  695.      */
  696.     D3DAppIGetClientWin(hwnd);
  697.     w = LOWORD(lParam);
  698.     h = HIWORD(lParam);
  699.     /*
  700.      * If w and h are under the minimum, create buffers of the minimum size
  701.      */
  702.     if (w < D3DAPP_WINDOWMINIMUM)
  703.         w = D3DAPP_WINDOWMINIMUM;
  704.     if (h < D3DAPP_WINDOWMINIMUM)
  705.         h = D3DAPP_WINDOWMINIMUM;
  706.     /*
  707.      * Destroy the viewport and all execute buffers
  708.      */
  709.     d3dappi.bRenderingIsOK = FALSE;
  710.     ATTEMPT(D3DAppICallDeviceDestroyCallback());
  711.     /*
  712.      * Only create a new device and buffers if they changed significantly,
  713.      * otherwise just make sure the old buffers aren't lost.
  714.      */
  715.     if ((w > szBuffers.cx || h > szBuffers.cy) ||
  716.     (w < szBuffers.cx / 2 || h < szBuffers.cy / 2)) {
  717.     /*
  718.      * Release the device
  719.      */
  720.     RELEASE(d3dappi.lpD3DDevice);
  721.     /*
  722.      * Release the old buffers
  723.      */
  724.     RELEASE(d3dappi.lpZBuffer);
  725.     RELEASE(lpPalette);
  726.     RELEASE(lpClipper);
  727.     RELEASE(d3dappi.lpBackBuffer);
  728.     RELEASE(d3dappi.lpFrontBuffer);
  729.     /*
  730.      * Create new ones
  731.      */
  732.     ATTEMPT(D3DAppICreateBuffers(hwnd, w, h, D3DAPP_BOGUS, FALSE, d3dappi.ThisDriver.bIsHardware));
  733.     ATTEMPT(D3DAppICheckForPalettized());
  734.     ATTEMPT(D3DAppICreateZBuffer(w, h, d3dappi.CurrDriver));
  735.     /*
  736.      * Create the driver
  737.      */
  738.     ATTEMPT(D3DAppICreateDevice(d3dappi.CurrDriver));
  739.     /*
  740.      * Since the driver did not change, the texture surfaces are still valid.
  741.      * We just need to get new handles.
  742.      */
  743.     if (d3dappi.ThisDriver.bDoesTextures) {
  744.         for (i = 0; i < d3dappi.NumUsableTextures; i++) {
  745.         D3DAppIGetTextureHandle(i);
  746.         }
  747.     }
  748.     } else {
  749.     D3DAppCheckForLostSurfaces();
  750.     }
  751.     /*
  752.      * Call the device create callback to create the viewport, set the render
  753.      * state and clear the dirty rectangle info
  754.      */
  755.     ATTEMPT(D3DAppICallDeviceCreateCallback(w, h));
  756.     ATTEMPT(D3DAppISetRenderState());
  757.     D3DAppIValidateDirtyRects();
  758.     d3dappi.bRenderingIsOK = TRUE;
  759.     /*
  760.      * Call the default window proc
  761.      */
  762.     *lresult = DefWindowProc(hwnd, message, wParam, lParam);
  763.     return TRUE;
  764. exit_with_error:
  765.     D3DAppICallDeviceDestroyCallback();
  766.     RELEASE(d3dappi.lpD3DDevice);
  767.     RELEASE(d3dappi.lpZBuffer);
  768.     RELEASE(lpPalette);
  769.     RELEASE(lpClipper);
  770.     RELEASE(d3dappi.lpBackBuffer);
  771.     RELEASE(d3dappi.lpFrontBuffer);
  772.     return FALSE;
  773. }
  774.  
  775. /***************************************************************************/
  776. /*              Setting the display mode and cooperative level             */
  777. /***************************************************************************/
  778. /*
  779.  * D3DAppISetCoopLevel
  780.  * Set the cooperative level to exclusive mode for fullscreen and normal for
  781.  * a window.  Set the bIgnoreWM_SIZE flag because SetCooperativeLevel
  782.  * generates a WM_SIZE message you do not have to resize the buffers on.
  783.  */
  784. BOOL
  785. D3DAppISetCoopLevel(HWND hwnd, BOOL bFullscreen)
  786. {
  787.     if (bFullscreen) {
  788.     bIgnoreWM_SIZE = TRUE;
  789.     LastError = d3dappi.lpDD->lpVtbl->SetCooperativeLevel(d3dappi.lpDD,
  790.                    hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
  791.     bIgnoreWM_SIZE = FALSE;
  792.     if(LastError != DD_OK ) {
  793.         D3DAppISetErrorString("SetCooperativeLevel to fullscreen failed.\n%s",
  794.                   D3DAppErrorToString(LastError));
  795.         return FALSE;
  796.     }
  797.     d3dappi.bFullscreen = TRUE;
  798.     } else {
  799.     bIgnoreWM_SIZE = TRUE;
  800.         LastError = d3dappi.lpDD->lpVtbl->SetCooperativeLevel(d3dappi.lpDD,
  801.                              hwnd, DDSCL_NORMAL);
  802.     bIgnoreWM_SIZE = FALSE;
  803.     if(LastError != DD_OK ) {
  804.             D3DAppISetErrorString("SetCooperativeLevel to normal failed.\n%s",
  805.                   D3DAppErrorToString(LastError));
  806.             return FALSE;
  807.         }
  808.     d3dappi.bFullscreen = FALSE;
  809.     }
  810.     return TRUE;
  811. }
  812.  
  813. /*
  814.  * D3DAppISetDisplayMode
  815.  * Set the display mode to the given dimensions and bits per pixel.  The
  816.  * bIgnoreWM_SIZE message is set because the display change generates a
  817.  * WM_SIZE message which we don't want to resize the buffers on.
  818.  */
  819. BOOL
  820. D3DAppISetDisplayMode(int w, int h, int bpp)
  821. {
  822.     d3dappi.ThisMode.w = w; d3dappi.ThisMode.h = h;
  823.     d3dappi.ThisMode.bpp = bpp;
  824.     bIgnoreWM_SIZE = TRUE;
  825.     LastError = d3dappi.lpDD->lpVtbl->SetDisplayMode(d3dappi.lpDD, w, h,
  826.                              bpp);
  827.     bIgnoreWM_SIZE = FALSE;
  828.     if(LastError != DD_OK ) {
  829.         D3DAppISetErrorString("SetDisplayMode to %dx%dx%d failed\n%s",
  830.                   w, h, bpp, D3DAppErrorToString(LastError));
  831.         return FALSE;
  832.     }
  833.     return TRUE;
  834. }
  835.  
  836. /*
  837.  * D3DAppIRestoreDispMode
  838.  * Restores the display mode to the current windows display mode.  The
  839.  * bIgnoreWM_SIZE message is set because the display change generates a
  840.  * WM_SIZE message which we don't want to resize the buffers on.
  841.  */
  842. BOOL
  843. D3DAppIRestoreDispMode(void)
  844. {
  845.     bIgnoreWM_SIZE = TRUE;
  846.     LastError = d3dappi.lpDD->lpVtbl->RestoreDisplayMode(d3dappi.lpDD);
  847.     if (LastError != DD_OK) {
  848.     D3DAppISetErrorString("RestoreDisplayMode failed.\n%s",
  849.                   D3DAppErrorToString(LastError));
  850.     return FALSE;
  851.     }
  852.     bIgnoreWM_SIZE = FALSE;
  853.     return TRUE;
  854. }
  855.  
  856. /*
  857.  * D3DAppRememberWindowsMode
  858.  * Record the current display mode in d3dappi.WindowsDisplay
  859.  */
  860. BOOL
  861. D3DAppIRememberWindowsMode(void)
  862. {
  863.     DDSURFACEDESC ddsd;
  864.  
  865.     memset(&ddsd, 0, sizeof(DDSURFACEDESC));
  866.     ddsd.dwSize = sizeof(DDSURFACEDESC);
  867.     LastError = d3dappi.lpDD->lpVtbl->GetDisplayMode(d3dappi.lpDD, &ddsd);
  868.     if (LastError != DD_OK) {
  869.     D3DAppISetErrorString("Getting the current display mode failed.\n%s",
  870.                   D3DAppErrorToString(LastError));
  871.     return FALSE;
  872.     }
  873.     d3dappi.WindowsDisplay.w = ddsd.dwWidth;
  874.     d3dappi.WindowsDisplay.h = ddsd.dwHeight;
  875.     d3dappi.WindowsDisplay.bpp = ddsd.ddpfPixelFormat.dwRGBBitCount;
  876.     return TRUE;
  877. }
  878.  
  879. /***************************************************************************/
  880. /*                          Misc DD Utilities                              */
  881. /***************************************************************************/
  882.  
  883. /*
  884.  * D3DAppIClearBuffers
  885.  * Clear the front and back buffers to black
  886.  */
  887. BOOL
  888. D3DAppIClearBuffers(void)
  889. {
  890.     DDSURFACEDESC ddsd;
  891.     RECT dst;
  892.     DDBLTFX ddbltfx;
  893.     /*
  894.      * Find the width and height of the front buffer by getting its
  895.      * DDSURFACEDESC
  896.      */
  897.     if (d3dappi.lpFrontBuffer) {
  898.     LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpFrontBuffer);
  899.     if (LastError != DD_OK) {
  900.         D3DAppISetErrorString("Failure getting the surface description of the front buffer before clearing.\n%s",
  901.                   D3DAppErrorToString(LastError));
  902.         return FALSE;
  903.     }
  904.     /*
  905.      * Clear the front buffer to black
  906.      */
  907.     memset(&ddbltfx, 0, sizeof(ddbltfx));
  908.     ddbltfx.dwSize = sizeof(DDBLTFX);
  909.     SetRect(&dst, 0, 0, ddsd.dwWidth, ddsd.dwHeight);
  910.     LastError = d3dappi.lpFrontBuffer->lpVtbl->Blt(d3dappi.lpFrontBuffer,
  911.                             &dst, NULL, NULL, 
  912.                             DDBLT_COLORFILL | DDBLT_WAIT,
  913.                             &ddbltfx);
  914.     if (LastError != DD_OK) {
  915.         D3DAppISetErrorString("Clearing the front buffer failed.\n%s",
  916.                   D3DAppErrorToString(LastError));
  917.         return FALSE;
  918.     }
  919.     }
  920.     if (d3dappi.lpBackBuffer) {
  921.     /*
  922.      * Find the width and height of the back buffer by getting its
  923.      * DDSURFACEDESC
  924.      */
  925.     LastError = D3DAppIGetSurfDesc(&ddsd, d3dappi.lpBackBuffer);
  926.     if (LastError != DD_OK) {
  927.         D3DAppISetErrorString("Failure while getting the surface description of the back buffer before clearing.\n%s",
  928.                   D3DAppErrorToString(LastError));
  929.         return FALSE;
  930.     }
  931.     /*
  932.      * Clear the back buffer to black
  933.      */
  934.     memset(&ddbltfx, 0, sizeof(ddbltfx));
  935.     ddbltfx.dwSize = sizeof(DDBLTFX);
  936.     SetRect(&dst, 0, 0, ddsd.dwWidth, ddsd.dwHeight);
  937.     LastError = d3dappi.lpBackBuffer->lpVtbl->Blt(d3dappi.lpBackBuffer, &dst,
  938.                              NULL, NULL,
  939.                              DDBLT_COLORFILL | DDBLT_WAIT,
  940.                              &ddbltfx);
  941.     if (LastError != DD_OK) {
  942.         D3DAppISetErrorString("Clearing the front buffer failed.\n%s",
  943.                   D3DAppErrorToString(LastError));
  944.         return FALSE;
  945.     }
  946.     }
  947.     return TRUE;
  948. }
  949.  
  950. /*
  951.  * D3DAppIBPPToDDBD
  952.  * Convert an integer bit per pixel number to a DirectDraw bit depth flag
  953.  */
  954. DWORD
  955. D3DAppIBPPToDDBD(int bpp)
  956. {
  957.     switch(bpp) {
  958.     case 1:
  959.         return DDBD_1;
  960.     case 2:
  961.         return DDBD_2;
  962.     case 4:
  963.         return DDBD_4;
  964.     case 8:
  965.         return DDBD_8;
  966.     case 16:
  967.         return DDBD_16;
  968.     case 24:
  969.         return DDBD_24;
  970.     case 32:
  971.         return DDBD_32;
  972.     default:
  973.         return (DWORD)D3DAPP_BOGUS;
  974.     }
  975. }
  976.  
  977. /*
  978.  * D3DAppTotalVideoMemory
  979.  * Returns the amount of total video memory supported (not free)
  980.  */
  981. DWORD
  982. D3DAppTotalVideoMemory(void)
  983. {
  984.     DDCAPS DriverCaps, HELCaps;
  985.     memset (&DriverCaps, 0, sizeof(DDCAPS));
  986.     DriverCaps.dwSize = sizeof(DDCAPS);
  987.     memset (&HELCaps, 0, sizeof(DDCAPS));
  988.     HELCaps.dwSize = sizeof(DDCAPS);
  989.     LastError = d3dappi.lpDD->lpVtbl->GetCaps(d3dappi.lpDD, &DriverCaps,
  990.                           &HELCaps);
  991.     if (LastError != DD_OK) {
  992.     D3DAppISetErrorString("Getting DD capabilities failed while checking total video memory.\n%s",
  993.                   D3DAppErrorToString(LastError));
  994.     return 0L;
  995.     }
  996.     if (DriverCaps.dwVidMemTotal)
  997.     return DriverCaps.dwVidMemTotal;
  998.     else
  999.     return HELCaps.dwVidMemTotal;
  1000. }
  1001.