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