D3DWIN.CPP
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWin.cpp 
**  Purpose:    Shows how to set up a windowed D3D framework 
**  Notes: 
** 
**Basic Initialization proceeds as follows: 
** 
**1.  Enumerate all Driver, modes, D3D devices (see DrvMgr.cpp for details) 
**2.  Choose a starting driver, mode, D3D device 
**- default driver = primary display driver (lpGuidDD = NULL) 
**- default mode   = current desktop 
**- default device = D3D device compatible with desktop mode 
**3.  Validate driver, mode, D3D device 
**4.  Create interfaces (from DD driver) 
**  5.  Set window (from associated window handle) 
**  6.  Create DD/D3D interfaces (lpDD, lpDD2, lpD3D) 
**  7.  Create Primary surface (primary palette, if necessary) 
**- Attach a clipper to primary surface 
**  8.  Create Render surface  
**- Render surface (and associated Z-buffer) 
**- D3D Device  
**- D3D Viewport 
** 
**  After initialization is complete, we have the 
**following objects necessary for rendering: 
** 
**lpDD2 - DirectDraw interface, used for creating texture surfaces 
**lpD3D - Direct3D interface, used for creating materials, lights, viewports 
**lpD3DDevice - D3D device (current material, current viewport, etc.) 
**lpViewport - current viewport 
**lpPrimary  - front buffer 
**lpRender   - render target 
** 
**  Copyright (c) 1995 - 1997 by Microsoft, all rights reserved 
**----------------------------------------------------------------------------- 
*/ 
 
/* 
**----------------------------------------------------------------------------- 
** Includes 
**----------------------------------------------------------------------------- 
*/ 
 
#include "D3DWin.h" 
#include "WinProc.h" 
#include "D3DScene.h" 
#include "Debug.h" 
 
 
 
/* 
**----------------------------------------------------------------------------- 
**D3DWindow Methods 
**----------------------------------------------------------------------------- 
*/ 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::D3DWindow 
** Purpose: Default Constructor 
**----------------------------------------------------------------------------- 
*/ 
 
D3DWindow::D3DWindow (void) 
{ 
    ZeroMemory (this, sizeof(D3DWindow)); 
    this->dwSize = sizeof(D3DWindow); 
 
// Default to creating a z-buffer 
createZBufferOn (); 
} // End D3DWindow::D3DWindow () 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::~D3DWindow 
** Purpose: Destructor 
**----------------------------------------------------------------------------- 
*/ 
 
D3DWindow::~D3DWindow (void) 
{ 
// Destroy all objects 
    Fini (); 
 
// Mark all other pointers as invalid 
// In case user tries to reuse this object incorrectly 
lpCurrDriver = NULL; 
lpCurrMode = NULL; 
lpCurrDevice = NULL; 
hWindow = NULL; 
lpd3dScene = NULL; 
 
} // End D3DWindow::~D3DWindow 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Create 
** Purpose: Creates a D3DWindow  
** 
** Basic Algorithm: 
**- Validate parameters 
**- Choose (and validate choices) for driver, mode, device 
**- Create Interfaces 
**- Set Window 
**- Create Primary surface (and palette) 
**- Create Render surface (and D3D device) 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Create ( 
HWND   hWnd,/* In:  Window */ 
LPGUID lpGuidDD,/* In:  Requested DirectDraw Device */ 
DWORD  dwW,/* In:Requested Mode */ 
DWORD  dwH, 
DWORD  dwBPP, 
DWORD  dwRefresh, 
LPGUID lpGuidD3D,/* In:  Requested D3D device */ 
BOOL   fUseZBuffer, /* In:Create Z-buffer */ 
LPRECT lprSurf)/* In:  requested surface size (NULL => use client area) */ 
{ 
    HRESULT         hResult; 
 
    // Check parameters 
if ((! hWnd) || (! IsWindow (hWnd))) 
{ 
REPORTERR (DDERR_INVALIDPARAMS); 
return DDERR_INVALIDPARAMS; 
} 
 
// Set Current Window 
hWindow = hWnd; 
 
    // Set Use Z-Buffer On/Off 
    if (fUseZBuffer) 
        createZBufferOn (); 
    else 
        createZBufferOff (); 
 
    // Choose Default Driver, Mode, device  
    hResult = ChooseDriverDefaults (lpGuidDD, 
dwW, dwH, dwBPP, dwRefresh, 
lpGuidD3D, 
FALSE, 
&lpCurrDriver, 
&lpCurrMode, 
&lpCurrDevice); 
    if (FAILED (hResult)) 
        return hResult; 
     
    // Create DD/D3D Interface objects 
    hResult = InitInterfaces (); 
    if (FAILED (hResult)) 
        return hResult; 
 
// Attach window to DD interface 
hResult = InitWindow (); 
if (FAILED (hResult)) 
goto lblCLEANUP; 
 
    // Create Primary Surface (and palette) 
    hResult = InitPrimary (); 
    if (FAILED (hResult)) 
        goto lblCLEANUP; 
 
// Get initial surface size 
if (lprSurf) 
rSurf = *lprSurf; 
else 
GetClientRect (hWindow, &rSurf); 
 
// Create the Render Surface (and D3D Device) 
    hResult = InitRender (); 
    if (FAILED (hResult)) 
        goto lblCLEANUP; 
 
// Notify the window of a successful initialization 
SendMessage (hWindow, D3DWIN_INIT, 0, (LPARAM)(void *)this); 
 
    // Success 
    return DD_OK; 
 
lblCLEANUP: 
    // Cleanup 
    Fini (); 
 
    return hResult; 
} // End D3DWindow::Create 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Init 
** Purpose:  
** 
** Basic Algorithm: 
**- Validate driver, mode, device 
**- Create Interfaces 
**- Attach Window 
**- Create Primary surface (and palette) 
**- Create Render surface (and D3D device) 
** 
** Notes: 
**1.  Assumes that a valid window handle has already 
**been associated with this D3DWindow 
**2.  Assumes that driver, mode, device already choosen 
**- however if not, reasonable defaults will be choosen 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Init (void) 
{ 
    HRESULT         hResult; 
 
    // Check parameters 
if ((! hWindow) || (! IsWindow (hWindow))) 
{ 
// Error, we absolutely need a valid window 
REPORTERR (DDERR_INVALIDPARAMS); 
return DDERR_INVALIDPARAMS; 
} 
 
    // Validate Curr Driver, mode, device 
hResult = ValidateDefaults (); 
if (FAILED (hResult)) 
return hResult; 
 
    // Create DD/D3D Interface objects 
    hResult = InitInterfaces (); 
    if (FAILED (hResult)) 
goto lblCLEANUP; 
 
// Attach the window to the DD interface 
hResult = InitWindow (); 
if (FAILED (hResult)) 
goto lblCLEANUP; 
 
    // Create Primary Surface (and palette) 
    hResult = InitPrimary (); 
    if (FAILED (hResult)) 
        goto lblCLEANUP; 
 
// Create Render surface (and D3D device) 
    hResult = InitRender (); 
    if (FAILED (hResult)) 
        goto lblCLEANUP; 
 
// Notify the window of a successful initialization 
SendMessage (hWindow, D3DWIN_INIT, 0, (LPARAM)(void *)this); 
 
    // Success 
    return DD_OK; 
 
lblCLEANUP: 
    // Cleanup 
    Fini (); 
    return hResult; 
} // End D3DWindow::Init 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Fini 
** Purpose: Destroys a D3DWindow 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Fini (void) 
{ 
// Notify the window that we are cleaning up 
SendMessage (hWindow, D3DWIN_FINI, 0, (LPARAM)(void *)this); 
 
// Cleanup 
    FiniRender (); 
    FiniPrimary (); 
//FiniWindow (); 
    FiniInterfaces (); 
 
// Success 
return DD_OK; 
} // End D3DWindow::Fini 
 
 
   
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::ValidateDefaults 
** Purpose: Verify's current driver, mode, and device 
** Notes:    
** 
**1.  Rather than fail completely, this will pick new defaults 
**      if the current defaults don't work. 
** 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::ValidateDefaults (void) 
{ 
LPGUIDlpGuidDD, lpGuidD3D; 
HRESULThResult; 
    LPDDDrvInfolpDrvNew; 
LPDDModeInfolpModeNew; 
LPD3DDevInfolpDevNew; 
 
// Initialize Driver Manager, if necessary 
if (! DDDrvMgr::isInitialized ()) 
{ 
hResult = DDDrvMgr::Init (); 
if (FAILED (hResult)) 
return DDERR_INVALIDPARAMS; 
} 
     
    // Get DD Guid 
if (lpCurrDriver) 
lpGuidDD = lpCurrDriver->GetGuid (); 
else 
lpGuidDD = NULL; 
 
// Get D3D Guid 
if (lpCurrDevice) 
lpGuidD3D = &(lpCurrDevice->guid); 
else 
lpGuidD3D = NULL; 
 
// Get Driver corresponding to DD Guid 
    lpDrvNew = ValidateDriver (lpGuidDD); 
if (! lpDrvNew) 
    { 
        // Error, invalid DD Guid 
REPORTERR (DDERR_INVALIDPARAMS); 
        return DDERR_INVALIDPARAMS; 
    } 
 
// Get Desktop mode and compatible D3D device 
if (! GetDesktopMode (lpDrvNew, lpGuidD3D, &lpModeNew, &lpDevNew)) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
// Note:  Instead of complaining let's go ahead 
//  and use the new defaults 
// Save new defaults 
lpCurrDriver = lpDrvNew; 
lpCurrMode = lpModeNew; 
lpCurrDevice = lpDevNew; 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::ValidateDefaults 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::CreateInterfaces 
** Purpose: Creates DD/D3D interfaces from specified Guid 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::CreateInterfaces (LPGUID lpDDGuid) 
{ 
LPDDDrvInfo lpDrvNew; 
HRESULThResult; 
 
// Verify Guid 
lpDrvNew = ValidateDriver (lpDDGuid); 
if (! lpDrvNew) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
lpCurrDriver = lpDrvNew; 
 
hResult = D3DWindow::InitInterfaces (); 
if (FAILED (hResult)) 
return hResult; 
 
// Success 
return DD_OK; 
} // End D3DWindow::CreateInterfaces 
 
   
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::InitInterfaces 
** Purpose: Creates DD/D3D interfaces 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitInterfaces (void) 
{ 
    HRESULT         hResult; 
    LPGUID          lpGuid; 
 
    // Do we have a current DD Driver 
    if (! lpCurrDriver) 
    { 
// Try to grab the Primary DD driver instead 
lpCurrDriver = ValidateDriver (NULL); 
if (! lpCurrDriver) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
    } 
 
    // Get DD Guid 
    lpGuid = lpCurrDriver->GetGuid (); 
     
    // Create DD interface 
    hResult = DirectDrawCreate (lpGuid, &lpDD, NULL); 
    if (FAILED (hResult)) 
    { 
        // Error 
REPORTERR (hResult); 
goto lblERROR; 
    } 
 
    // Get DD2 interface 
    hResult = lpDD->QueryInterface ((REFIID)IID_IDirectDraw2, (void **)&lpDD2); 
    if (FAILED (hResult)) 
    { 
        // Error 
REPORTERR (hResult); 
        goto lblERROR; 
    } 
 
    // Get D3D interface 
    hResult = lpDD2->QueryInterface ((REFIID)IID_IDirect3D2, (void **)&lpD3D); 
    if (FAILED (hResult)) 
    { 
        // Error 
REPORTERR (hResult); 
        goto lblERROR; 
    } 
 
// Mark this stage as done 
turnValidInterfaceOn (); 
 
    // Success 
    return DD_OK; 
 
lblERROR: 
    // Failure 
    FiniInterfaces (); 
 
    return hResult; 
} // End InitInterfaces 
 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::FiniInterfaces 
** Purpose: Destroys DD/D3D interfaces 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniInterfaces (void) 
{ 
// Mark this stage as invalid 
turnValidInterfaceOff (); 
 
    // Release Direct3D Interface 
    if (lpD3D) 
    { 
        lpD3D->Release (); 
        lpD3D = NULL; 
    } 
 
    // Release DirectDraw2 Interface 
    if (lpDD2) 
    { 
        lpDD2->Release (); 
        lpDD2 = NULL; 
    } 
 
    // Release DirectDraw Interface 
    if (lpDD) 
    { 
        lpDD->Release (); 
        lpDD = NULL; 
    } 
 
// Success 
return DD_OK; 
} // End D3DWindow::FiniInterfaces 
 
 
 
/* 
**----------------------------------------------------------------------------- 
**Name:D3DWindow::InitWindow 
**Purpose:Attaches Window to Direct Draw Interface 
**Notes:Assumes window handle already associated with this D3DWindow. 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitWindow (void) 
{ 
HRESULT hResult; 
DWORDdwFlags; 
 
// Check Initialization 
if ((! hWindow) || (! IsWindow (hWindow))) 
{ 
// Error, we have to have an associated window to continue 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
    // Get Cooperative Flags 
    dwFlags = DDSCL_NORMAL; 
 
    // Set Cooperative Level 
    hResult = lpDD2->SetCooperativeLevel (hWindow, dwFlags); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
return hResult; 
    } 
 
// Success 
return DD_OK; 
} // End D3DWindow::InitWindow 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::FiniWindow 
** Purpose: Cleanups window 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniWindow (void) 
{ 
// Currently does nothing 
 
// Success 
return DD_OK; 
} // End D3DWindow::FiniWindow 
 
 
   
/* 
**----------------------------------------------------------------------------- 
**Name:    D3DWindow::InitPrimary 
**Purpose: Creates a primary surface 
** 
**Basic Algorithm: 
**1.  Create primary surface (from desktop mode) 
**2.  Create and attach a clipper object 
**3.  Create and attach a palette object, if desktop is palettized 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitPrimary (void) 
{ 
    HRESULThResult; 
    DDSURFACEDESCddsd; 
    LPDIRECTDRAWCLIPPER lpddcClipper = NULL;     
 
    // Check Initialization 
    if ((! lpCurrMode) || (! lpDD2)) 
    { 
        // Error 
REPORTERR (DDERR_GENERIC); 
        return DDERR_GENERIC; 
    } 
 
//  
// Step 1.  Create Primary Surface 
// 
 
// Note:  No need to fill in width, height, etc. 
//        They system knows how to do it automatically 
//  for a primary surface 
    ddsd.dwSize         = sizeof(ddsd); 
    ddsd.dwFlags        = DDSD_CAPS; 
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; 
 
// Create Primary surface 
    hResult = lpDD2->CreateSurface (&ddsd, &lpddsPrimary, NULL); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
 
//  
// Step 2.   Create and attach clipper 
// 
// Note:This needs to be done to prevent drawing outside 
//of the windows current client area 
 
// Create Clipper object 
    hResult = DirectDrawCreateClipper (0L, &lpddcClipper, 0); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
goto lblCLEANUP; 
    } 
     
// Attach window to clipper 
    hResult = lpddcClipper->SetHWnd (0L, hWindow); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
goto lblCLEANUP; 
    } 
 
// Attach clipper to primary surface 
    hResult = lpddsPrimary->SetClipper (lpddcClipper); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
goto lblCLEANUP; 
    } 
 
    // Go ahead and release the clipper interface,  
    // we don't need it anymore and it will be 
    // destroyed automatically with the surface 
    lpddcClipper->Release (); 
 
// Create and attach palette, if necessary 
hResult = InitPalette (); 
if (FAILED (hResult)) 
goto lblCLEANUP; 
 
// Mark as Valid 
turnValidPrimaryOn (); 
 
    // Success 
    return DD_OK; 
 
lblCLEANUP: 
// Failure 
 
// Cleanup before leaving 
if (lpddcClipper) 
lpddcClipper->Release (); 
 
FiniPrimary (); 
 
return hResult; 
} // End D3DWindow::InitPrimary 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::FiniPrimary 
** Purpose: Destroys the Primary Surface 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniPrimary (void) 
{ 
// Mark as Invalid 
turnValidPrimaryOff (); 
 
// Cleanup palette 
FiniPalette (); 
 
    // Release Primary Surface Object 
    if (lpddsPrimary) 
    { 
        lpddsPrimary->Release (); 
        lpddsPrimary = NULL; 
    } 
 
// Success 
return DD_OK; 
} // End D3DWindow::FiniPrimary 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::InitPalette 
** Purpose: Creates a primary palette if necessary 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitPalette () 
{ 
    HRESULT             hResult; 
    HDC                 hdc; 
    DWORD               ii; 
    PALETTEENTRY        pePalette[256]; 
    DWORD               cPal; 
    DWORD               cbSize; 
    DWORD               dwFlags; 
    DDSURFACEDESC       ddsd; 
 
    // Destroy old palette 
    FiniPalette (); 
 
    // Make sure we are properly intialized  
    // for this to work 
    if ((! lpDD2) || (! lpddsPrimary)) 
    { 
        // Error 
REPORTERR (DDERR_GENERIC); 
        return DDERR_GENERIC; 
    } 
 
    // Get primary surface caps 
    ZeroMemory(&ddsd, sizeof(ddsd)); 
    ddsd.dwSize = sizeof(ddsd); 
    hResult = lpddsPrimary->GetSurfaceDesc(&ddsd); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
    // Make sure it is a palettized surface 
    if (! isPalettized (&(ddsd.ddpfPixelFormat))) 
    { 
        // Success, primary isn't palettized 
// So we don't need to create a palette 
        return DD_OK; 
    } 
 
    // Create and save System palette 
    hdc = GetDC (NULL); 
    cPal = GetDeviceCaps (hdc, SIZEPALETTE); 
    if (cPal) 
    { 
        if (cPal > 256) 
            cPal = 256; 
 
        // Get memory for palette entries 
        lppePalette = new PALETTEENTRY[cPal]; 
        if (! lppePalette) 
        { 
            // Error, not enough memory 
ReleaseDC (NULL, hdc); 
 
hResult = DDERR_OUTOFMEMORY; 
REPORTERR (hResult); 
goto lblCLEANUP; 
        } 
 
        // Save system palette 
        GetSystemPaletteEntries (hdc, 0, cPal, lppePalette); 
 
        // Copy system palette to temporary values 
        cbSize = cPal * sizeof (PALETTEENTRY); 
        CopyMemory (pePalette, lppePalette, cbSize); 
    } 
ReleaseDC (NULL, hdc); 
 
    if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1) 
    { 
        dwFlags = DDPCAPS_1BIT; 
 
        // Only 2 palette entries, we need them all 
        for (ii = 0; ii < 2; ii++) 
            pePalette[ii].peFlags = D3DPAL_FREE | PC_RESERVED; 
 
    } 
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2) 
    { 
        // Only 4 palette entries, we need them all 
        for (ii = 0; ii < 4; ii++) 
            pePalette[ii].peFlags = D3DPAL_FREE | PC_RESERVED; 
 
        dwFlags = DDPCAPS_2BIT; 
    } 
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4) 
    { 
        // Only 16 palette entries, we will save black and white 
        // and keep the rest for ourselves. 
 
        pePalette[0].peFlags = D3DPAL_READONLY; 
        pePalette[15].peFlags = D3DPAL_READONLY; 
 
        for (ii = 1; ii < 15; ii++) 
            pePalette[ii].peFlags = D3DPAL_FREE | PC_RESERVED; 
 
        dwFlags = DDPCAPS_4BIT; 
    } 
    else if (ddsd.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) 
    { 
        // 256 palette entries, we can afford to be nice 
        // and save the first 10 and last 10 palette entries 
        // for system use  
        for (ii = 0; ii < 10; ii++) 
        { 
            pePalette[ii].peFlags = D3DPAL_READONLY; 
            pePalette[246+ii].peFlags = D3DPAL_READONLY; 
        } 
 
        for (ii = 10; ii < 246; ii++) 
            pePalette[ii].peFlags = D3DPAL_FREE | PC_RESERVED; 
 
        dwFlags = DDPCAPS_8BIT;         
    } 
    else 
    { 
        // Error, programming (unknown palette type) 
hResult = DDERR_GENERIC; 
REPORTERR (hResult); 
goto lblCLEANUP; 
    } 
 
    // Create Primary Palette 
    hResult = lpDD2->CreatePalette (dwFlags, 
                                    pePalette, 
                                    &lpddpPalette, 
                                    NULL); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
        goto lblCLEANUP; 
    } 
 
    // Attach palette to primary surface 
    hResult = lpddsPrimary->SetPalette (lpddpPalette); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
        goto lblCLEANUP; 
    } 
 
    // Success 
    return DD_OK; 
 
lblCLEANUP: 
// Cleanup before leaving 
    FiniPalette (); 
 
// Failure 
return hResult; 
} // D3DWindow::InitPalette 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::FiniPalette 
** Purpose: Destroys primary palette 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniPalette (void) 
{ 
    // Note:  Should we Detach Palette object from surfaces 
    // No way to do this that I know of... 
     
    // Cleanup up DD Palette object 
    if (lpddpPalette) 
    { 
        lpddpPalette->Release (); 
        lpddpPalette = NULL; 
    } 
 
    // Cleanup System Palette 
    if (lppePalette) 
    { 
        // Note:  Should we try and restore system palette here ?!? 
 
        // Destroy system palette 
        delete [] lppePalette; 
        lppePalette = NULL; 
    } 
 
// Success 
return DD_OK; 
} // End FiniPalette 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::ValidateSize 
** Purpose: Validate Surface Size 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::ValidateSize (void) 
{ 
long x, y, w,h; 
    long wwMax, whMax;  // maximum windows width, height 
    long wwMin, whMin;  // minimum windows width, height 
 
// Get Min, max window sizes 
    wwMax = GetSystemMetrics (SM_CXMAXTRACK); 
    whMax = GetSystemMetrics (SM_CYMAXTRACK); 
    wwMin = GetSystemMetrics (SM_CXMIN); 
    whMin = GetSystemMetrics (SM_CYMIN); 
 
// Null Size => use client area of window for size 
w = abs (rSurf.right - rSurf.left); 
h = abs (rSurf.bottom - rSurf.top); 
if ((w == 0) || (h == 0)) 
{ 
if ((! hWindow) || (! IsWindow (hWindow))) 
return DDERR_GENERIC; 
 
GetClientRect (hWindow, &rSurf); 
} 
 
// Get canonical pos, size 
if (rSurf.right < rSurf.left) 
{ 
x = rSurf.right; 
w = rSurf.left - rSurf.right; 
} 
else 
{ 
x = rSurf.left; 
w = rSurf.right - rSurf.left; 
} 
 
if (rSurf.bottom < rSurf.top) 
{ 
y = rSurf.bottom; 
h = rSurf.top - rSurf.bottom; 
} 
else 
{ 
y = rSurf.top; 
h = rSurf.bottom - rSurf.top; 
} 
 
 
// Clamp to min,max sizes 
if (w < D3DWIN_MIN_SIZE) 
w = D3DWIN_MIN_SIZE; 
if (w < wwMin) 
w = wwMin; 
if (w > wwMax) 
w = wwMax; 
 
if (h < D3DWIN_MIN_SIZE) 
h = D3DWIN_MIN_SIZE; 
if (h < whMin) 
h = whMin; 
if (h > whMax) 
h = whMax; 
 
// Save validated surface 
rSurf.left   = x; 
rSurf.right  = x + w; 
rSurf.top    = y; 
rSurf.bottom = y + h; 
 
// Success 
return DD_OK; 
} // End D3DWindow::ValidateSize 
 
 
     
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::CreateRender 
** Purpose: Creates the rendering surface from requested device and  
**surface size 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::CreateRender (LPGUID lpD3DGuid, LPRECT lprSurf) 
{ 
HRESULT hResult; 
LPD3DDevInfo lpDevNew; 
 
// Check Initialization 
if ((! lpCurrDriver) || (! lpCurrMode)) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
// Make sure this D3D device works with this mode 
lpDevNew = ValidateDevice (lpCurrDriver, lpD3DGuid, lpCurrMode); 
if (! lpDevNew) 
{ 
REPORTERR (DDERR_INVALIDPARAMS); 
return DDERR_INVALIDPARAMS; 
} 
// Save new D3D device 
lpCurrDevice = lpDevNew; 
 
 
// Get Surface Size 
if (! lprSurf) 
GetClientRect (hWindow, &rSurf); 
else 
rSurf = *lprSurf; 
 
// Cleanup old render surface 
FiniRender (); 
 
// Create new Render surface (using new Device) 
hResult = InitRender (); 
if (FAILED (hResult)) 
return hResult; 
 
// Success 
return DD_OK; 
} 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::InitRender 
** Purpose: Creates the rendering surface and D3D device 
** Notes: 
** 
**1. The main problem here is a catch 22 situation.  In order to create the 
**   D3D device you need a valid render surface.   However, in order to create 
**     a render surface properly, you need to know some D3D device caps  
**- Does the device support Z-buffers 
**- Video vs. System memory 
**- etc. 
** 
**   Fortunately, our Driver Manager already has all that information. 
**   So we can break the catch 22.  Without having to resort to some extra 
**     complexity in the code here. 
** 
**  
** Basic Algorithm: 
** 
**  1. Validate Device 
**  2. Validate Surface size 
**  3. Setup Surface Desc (width, height, bpp) 
**4. Create Render Surface (from surface desc) 
**  5. Create Z-buffer (optional) 
**6. Create D3D Device 
**7. Create Viewport 
**8. InitScene (optional) 
** 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitRender (void) 
{ 
    HRESULT         hResult; 
    DWORD           dwMemType; 
    LPD3DDEVICEDESC lpDeviceDesc; 
    DDSURFACEDESC   ddsd; 
    DWORD           dwWidth, dwHeight, dwBPP; 
LPGUIDlpGuidD3D; 
LPD3DDevInfolpDevNew; 
 
// Check Initialization 
    if ((! hWindow) || (! IsWindow (hWindow)) || 
(! lpCurrDevice) || (! lpCurrMode) ||  
(! lpDD2) || (! lpD3D) || (! lpddsPrimary)) 
    { 
        // Error, Not initialized properly 
REPORTERR (DDERR_GENERIC); 
        return DDERR_GENERIC; 
    } 
 
 
// 
// Step 1.  Validate Device 
// 
if (lpCurrDevice) 
lpGuidD3D = &(lpCurrDevice->guid); 
else 
lpGuidD3D = NULL; 
 
lpDevNew = ValidateDevice (lpCurrDriver, lpGuidD3D, lpCurrMode); 
if (! lpDevNew) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
// Save new D3D device 
lpCurrDevice = lpDevNew; 
 
 
//  
// Step 2. Validate Surface size 
// 
hResult = ValidateSize (); 
if (FAILED (hResult)) 
return hResult; 
 
dwWidth = rSurf.right - rSurf.left; 
dwHeight = rSurf.bottom - rSurf.top; 
 
 
    // 
// Step 3.  Setup DD surface desc (width, height, BPP) 
// 
 
// Get Memory Type 
if (lpCurrDevice->isHardware ()) 
{ 
// Hardware device 
dwMemType = DDSCAPS_VIDEOMEMORY; 
lpDeviceDesc = &(lpCurrDevice->d3dHalDesc); 
} 
else 
{ 
// Software device  
// Note:  It doesn't have to be in system memory but 
//        it makes debugging a lot easier  
//  (Win16locks don't get in the way) 
dwMemType = DDSCAPS_SYSTEMMEMORY; 
lpDeviceDesc = &(lpCurrDevice->d3dHelDesc); 
} 
 
    // Get primary surface caps 
    ZeroMemory(&ddsd, sizeof(ddsd)); 
    ddsd.dwSize = sizeof (DDSURFACEDESC); 
    hResult = lpddsPrimary->GetSurfaceDesc (&ddsd); 
    if (FAILED (hResult)) 
    { 
      // Error 
      REPORTERR (hResult); 
      return hResult; 
    } 
 
    // Create device surface 
// Note: We don't need to specify the pixel format, it will 
//       automatically default to same format as primary 
    ddsd.dwSize         = sizeof(ddsd); 
    ddsd.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;// | DDSD_PIXELFORMAT; 
    ddsd.dwWidth        = dwWidth; 
    ddsd.dwHeight       = dwHeight; 
    ddsd.ddsCaps.dwCaps = DDSCAPS_3DDEVICE | DDSCAPS_OFFSCREENPLAIN | dwMemType; 
 
//  
// Step 2.  Create Rendering Surface (from dd surface desc) 
// 
hResult = lpDD2->CreateSurface (&ddsd, &lpddsRender, NULL); 
if (FAILED(hResult)) 
{ 
REPORTERR (hResult); 
return hResult; 
} 
 
// Attach palette, if any 
if (NULL != lpddpPalette) 
{ 
hResult = lpddsRender->SetPalette (lpddpPalette); 
if (FAILED (hResult)) 
{ 
// Error 
REPORTERR (hResult); 
return hResult; 
} 
} 
 
 
// 
// Step 3.Create and attach Z-buffer (optional) 
// 
    if ((isCreateZBuffer ()) &&  
        (0L != lpDeviceDesc->dwDeviceZBufferBitDepth)) 
    { 
        dwBPP = FlagsToBitDepth (lpDeviceDesc->dwDeviceZBufferBitDepth); 
 
        // Create the z-buffer. 
        ZeroMemory (&ddsd, sizeof(ddsd)); 
        ddsd.dwSize            = sizeof(ddsd); 
        ddsd.dwFlags           = DDSD_CAPS   | 
                                 DDSD_WIDTH  | 
                                 DDSD_HEIGHT | 
                                 DDSD_ZBUFFERBITDEPTH; 
        ddsd.ddsCaps.dwCaps    = DDSCAPS_ZBUFFER | dwMemType; 
        ddsd.dwWidth           = dwWidth; 
        ddsd.dwHeight          = dwHeight; 
        ddsd.dwZBufferBitDepth = dwBPP; 
        hResult = lpDD2->CreateSurface (&ddsd, &lpddsZBuff, NULL); 
        if (FAILED(hResult)) 
        { 
            // Note: we may be able to continue without a z buffer 
            REPORTERR (hResult); 
        } 
else 
{ 
// Attach Z-buffer to rendering surface 
hResult = lpddsRender->AddAttachedSurface (lpddsZBuff); 
if (FAILED (hResult)) 
{ 
// Note: we may be able to continue without a z buffer 
REPORTERR (hResult); 
if (lpddsZBuff) 
{ 
lpddsZBuff->Release (); 
lpddsZBuff = NULL; 
} 
} 
} 
    } 
 
 
    // 
    // Step 4.  Create the D3D device interface 
// 
hResult = lpD3D->CreateDevice (lpCurrDevice->guid, 
    lpddsRender,  
    &lpd3dDevice); 
    if (FAILED (hResult)) 
    { 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
 
// 
// Step 5.  Create the viewport 
// 
hResult = InitViewport (); 
if (FAILED (hResult)) 
return hResult; 
 
// Mark as valid 
turnValidRenderOn (); 
turnCalcRectsOn (); 
 
 
//  
// Step 6.Initialize Scene (if attached) 
// 
if (lpd3dScene) 
{ 
hResult = lpd3dScene->Init (this); 
if (FAILED (hResult)) 
return hResult; 
} 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::InitRender 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::FiniRender 
** Purpose: Destroys the Rendering surface 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniRender (void) 
{ 
// Mark as invalid 
turnValidRenderOff (); 
 
// Cleanup Associated Scene 
if (lpd3dScene) 
{ 
lpd3dScene->Fini (); 
} 
 
// Cleanup viewport 
FiniViewport (); 
 
    // Release D3D Device 
    if (lpd3dDevice) 
    { 
        lpd3dDevice->Release (); 
        lpd3dDevice = NULL; 
    } 
 
    // Release Z Buffer 
    if (lpddsZBuff) 
    { 
        // Detach Z-Buffer from back buffer 
        if (lpddsRender) 
            lpddsRender->DeleteAttachedSurface (0L, lpddsZBuff); 
        lpddsZBuff->Release (); 
        lpddsZBuff = NULL; 
    } 
 
    // Release rendering surface 
    if (lpddsRender) 
    { 
        lpddsRender->Release (); 
        lpddsRender = NULL; 
    } 
 
// Success 
return DD_OK; 
} // End D3DWindow::FiniRender 
 
 
 
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWindow::InitViewport 
**  Purpose:     
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::InitViewport (void) 
{ 
HRESULT hResult; 
 
// Check Initialization 
if ((! lpD3D) || (! lpd3dDevice)) 
{ 
        REPORTERR (DDERR_GENERIC); 
        return DDERR_GENERIC; 
} 
 
// Create Viewport 
    hResult = lpD3D->CreateViewport (&lpd3dViewport, NULL); 
    if (FAILED (hResult)) 
    { 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
// Attach viewport to D3D device 
    hResult = lpd3dDevice->AddViewport (lpd3dViewport); 
    if (FAILED (hResult)) 
    { 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
// Set up Initial Viewport parameters 
hResult = UpdateViewport (); 
    if (FAILED (hResult)) 
        return hResult; 
 
/// Success 
return DD_OK; 
} // End D3DWindow::InitViewport 
   
 
 
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWindow::FiniViewport 
**  Purpose:    Cleanup viewport 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::FiniViewport (void) 
{ 
// Release D3D viewport 
    if (lpd3dViewport) 
    { 
        lpd3dViewport->Release (); 
        lpd3dViewport  = NULL; 
    } 
 
// Success 
return DD_OK; 
} // End D3DWindow::FiniViewport 
 
   
 
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWindow::UpdateViewport 
**  Purpose:    Keeps viewport updated with current window size 
**  Notes: 
** 
**1. The viewport construction here assumes that you are rendering  
**Triangles using the D3DVERTEX and D3DIM is doing Transform, 
**lighting, and rasterization for you. 
** 
**2. If you are rendering triangles using D3DTLVERTEX and doing your 
**   own transform and lighting then you need to setup the viewport 
**   differently.   As follows: 
** 
**      // Replace the following values below: 
**dvClipX= 0.0f; 
**dvClipY= 0.0f; 
**dvClipWidth= dwSurfW; 
**dvClipHeight= dvSurfH; 
** 
**  3. This perserves the aspect ratio.  If you don't need or want to 
**     perserve the aspect ratio then set inv_aspect = 1.0 below and 
**     work this constant through the rest of the viewport setup. 
** 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::UpdateViewport (void) 
{ 
    HRESULThResult; 
    D3DVIEWPORT2d3dViewport; 
DWORDdwSurfW, dwSurfH; 
 
    // Check Parameters 
if ((! lpd3dDevice) || (! lpd3dViewport)) 
{ 
// Not properly initialized. 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
// Get Surface Width and Height 
dwSurfW = abs (rSurf.right - rSurf.left); 
dwSurfH = abs (rSurf.bottom - rSurf.top); 
 
float inv_aspect; 
 
if (dwSurfW) 
inv_aspect = (float)dwSurfH/(float)dwSurfW; 
else 
                inv_aspect = 1.0f; 
 
    // Update Viewport 
    ZeroMemory (&d3dViewport, sizeof(d3dViewport)); 
    d3dViewport.dwSize= sizeof(d3dViewport);     // Always set size of structure!!! 
    d3dViewport.dwX= 0UL; 
    d3dViewport.dwY= 0UL; 
    d3dViewport.dwWidth= dwSurfW; 
    d3dViewport.dwHeight= dwSurfH; 
    d3dViewport.dvClipX= -1.0f; 
    d3dViewport.dvClipY= inv_aspect; 
    d3dViewport.dvClipWidth= 2.0f; 
    d3dViewport.dvClipHeight = 2.0f * inv_aspect; 
    d3dViewport.dvMinZ= 0.0f; 
    d3dViewport.dvMaxZ= 1.0f; 
 
// Update Viewport 
    hResult = lpd3dViewport->SetViewport2 (&d3dViewport); 
    if (FAILED (hResult)) 
    { 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
// Update D3D device to use this viewport 
    hResult = lpd3dDevice->SetCurrentViewport (lpd3dViewport); 
    if (FAILED (hResult)) 
    { 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::UpdateViewport 
 
   
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::DrawFrame 
** Purpose: Paints current surface to window 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::DrawFrame (void) 
{ 
    HRESULT hResult = DD_OK; 
 
// Check Initialization 
    if (! isValid ()) 
    { 
        // Error, not properly initialized 
        return DDERR_GENERIC; 
    } 
 
    if (isPaused ()) 
    { 
        // Don't draw, if paused 
        return DD_OK; 
    } 
 
    // Double check parameters 
    if ((! lpddsPrimary) || (! lpddsRender)) 
    { 
        // Error, not properly initialized 
        return DDERR_GENERIC; 
    } 
     
    // Get current src, dest rectangles 
    CalcRects (); 
 
    if (! checkPaint ()) 
    { 
        // Nothing to draw, so return immediately 
        return DD_OK; 
    } 
 
    // Paint until we truly succeed or error out 
    while (TRUE) 
    { 
// Render D3D Scene 
if (lpd3dScene) 
hResult = lpd3dScene->Render (); 
 
if (SUCCEEDED (hResult)) 
{ 
// Blt Rendering surface onto primary surface 
hResult = lpddsPrimary->Blt (&rDrawDest,// Dest Rect 
                     lpddsRender,   // Src Surface 
                 &rDrawSrc,     // Src Rect 
             DDBLT_WAIT,    // Wait 
         NULL);         // Blt Special Effects (none) 
 
// Did it work ?!?   
if (SUCCEEDED (hResult)) 
{ 
// Success, exit 
return hResult; 
} 
} 
 
        // Check if busy or drawing 
        if ((DDERR_SURFACEBUSY == hResult) || 
            (DDERR_WASSTILLDRAWING == hResult)) 
        { 
            // Try again 
            continue; 
        } 
 
        // Check for lost surfaces 
        while (DDERR_SURFACELOST == hResult) 
        { 
            // Restore surfaces 
            hResult = Restore (); 
        } 
 
        // Check for real error 
        if (FAILED (hResult)) 
        { 
            // Error, 
            REPORTERR (hResult); 
            return hResult; 
        } 
    } 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::DrawFrame 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Move 
** Purpose: Move window to new coordinates 
** Notes:   This used to be simpler. (See CalcRects) 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Move (long x, long y) 
{ 
    // Need to recalculate src, dest rectangles 
    turnCalcRectsOn (); 
 
    // Success 
    return DD_OK; 
} // D3DWindow::Move 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Resize 
** Purpose: Resizes the Window 
** Notes: 
**We need to resize the render surface to stay in sync 
**with the new window size 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Resize (DWORD dwWidth, DWORD dwHeight) 
{ 
HRESULThResult; 
RECTrSurfOld; 
 
    // Need to recalculate src, dest rectangles  
    turnCalcRectsOn (); 
 
// Save old Surface coordinates 
rSurfOld = rSurf; 
 
// Update the surface rectangle 
rSurf.left   = 0; 
rSurf.top    = 0; 
rSurf.right  = dwWidth; 
rSurf.bottom = dwHeight; 
 
// Check if properly initialized 
if (! isValid ()) 
{ 
// Try to re-initialize 
Fini (); 
hResult = Init (); 
if (FAILED (hResult)) 
return hResult; 
 
// Successful re-initialization 
return DD_OK; 
} 
 
    // Cleanup Render Surface (and D3D Device) 
    FiniRender (); 
 
// Re-create Render Surface (at new size) 
hResult = InitRender (); 
if (FAILED (hResult)) 
{ 
if ((hResult == DDERR_OUTOFMEMORY) ||  
(hResult == DDERR_OUTOFVIDEOMEMORY)) 
{ 
// Try to restore old surface size 
rSurf = rSurfOld; 
 
FiniRender (); 
hResult = InitRender (); 
if (FAILED (hResult)) 
return hResult; 
 
// Successfully returned to previous size 
return DD_OK; 
} 
 
return hResult; 
} 
 
// Success 
return DD_OK; 
} // End D3DWindow::Resize  
 
 
 
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWindow::RealizePalette 
**  Purpose:     
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::RealizePalette (void) 
{ 
HRESULT hResult; 
 
// 
    // Realizing the palette using DirectDraw is quite different 
    // from GDI. To realize the palette we call SetPalette() 
    // each time our application is activated. 
    // 
    // NOTE: DirectDraw spots the fact that the new palette is the 
    // same as the old one and so does not increase the reference 
    // count of the palette. 
    // 
 
if ((lpddsPrimary) && (lpddpPalette)) 
{ 
hResult = lpddsPrimary->SetPalette (lpddpPalette); 
if (FAILED (hResult)) 
        { 
            REPORTERR (hResult); 
            return hResult; 
        } 
} 
 
// Success 
return DD_OK; 
} // End D3DWindow::RealizePalette 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Pause 
** Purpose: Pause any work on DD/D3D resources 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Pause (BOOL fPause) 
{ 
    // Turning pausing on/off ?!? 
    if (fPause) 
    { 
        if (dwPaused == 0L) 
        { 
// Very first time paused, do anything special here... 
 
// Force screen redraw 
if (hWindow) 
InvalidateRect (hWindow, NULL, FALSE); 
        } 
        dwPaused++; 
    } 
    else 
    { 
        if (dwPaused == 0L) 
        { 
            // Programmer Error, already paused 
REPORTERR (DDERR_GENERIC); 
            return DDERR_GENERIC; 
        } 
 
        // Decrement semaphore 
        dwPaused--; 
        if (dwPaused == 0L) 
        { 
// Very last time unpaused, do anything special here... 
 
// Force screen redraw 
if (hWindow) 
InvalidateRect (hWindow, NULL, FALSE); 
        } 
    } 
 
    // Success 
    return DD_OK; 
} // D3DWindow::Pause 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::AttachScene 
** Purpose:  
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::AttachScene (LPD3DScene lpNewScene) 
{ 
// Check parameters 
if (! lpNewScene) 
{ 
return DDERR_INVALIDPARAMS; 
} 
 
DetachScene (); 
 
// Save Scene pointer 
lpd3dScene = lpNewScene; 
 
// Initialize Scene ?!? 
if (isValid ()) 
{ 
lpd3dScene->Init (this); 
} 
 
// Success 
return DD_OK; 
} // End D3DWindow::AttachScene 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::DetachScene 
** Purpose:  
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::DetachScene (void) 
{ 
// Cleanup Scene 
if (lpd3dScene) 
{ 
lpd3dScene->Fini (); 
lpd3dScene = NULL; 
} 
 
// Success 
return DD_OK; 
} // End D3DWindow::DetachScene 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::CalcRects 
** Purpose: Recalculate src,dest rectangles 
** 
** Notes:Without this protection we get a GPF when we drag the window 
**off the edge of the screen.  This is because we are trying to 
**write pixels to a non-existent location.   The clipper object 
**attached to the primary protects the primary surface from  
**stray pixels outside of the window's client area.  However, it 
**doesn't stop us from trying to write outside the primary.  So 
**we need to protect ourselves. 
**  
** Basic Algorithm:    
** 
**     - Get Client rectangle 
**     - Get Render Surface rectangle 
**     - Intersect client with surface to get initial src rect 
** 
**     - Get Primary surface rectangle 
**     - Map Src rect to primary surface coordinates 
** 
**     - Intersect primary with src to get dest rect. 
**     - Map dest rect back into surface coordinates to get final src rect. 
** 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::CalcRects (void) 
{ 
    // Do we need to recalculate the rectangles 
// I.E. Can we avoid doing work 
    if (checkCalcRects ()) 
    { 
        HRESULT       hResult; 
        DDSURFACEDESC ddsd;         // Surface desc. 
        POINT         pnt; 
        RECT          rClient; 
        RECT          rSrc; 
        RECT          rDest; 
        RECT          rTemp; 
         
        if (! isValid ()) 
        { 
            // Error, not properly initialized 
            return DDERR_GENERIC; 
        } 
 
        // Assume we are going to paint 
        turnPaintOn (); 
 
        // Get Client rectangle pos/size 
        GetClientRect (hWindow, &rClient); 
 
        // Get Surface rectangle size 
        ddsd.dwSize = sizeof (DDSURFACEDESC); 
        hResult = lpddsRender->GetSurfaceDesc (&ddsd); 
        if (FAILED (hResult)) 
        { 
            // Error 
            REPORTERR (hResult); 
            return hResult; 
        } 
        rSurf.right     = rSurf.left + ddsd.dwWidth; 
        rSurf.bottom    = rSurf.top + ddsd.dwHeight; 
 
        // Intersect client with surface to get initial Src Rect 
        if (! IntersectRect (&rSrc, &rClient, &rSurf)) 
        { 
            // No intersection, means nothing to draw 
            turnPaintOff (); 
            rSrc.left   = 0; 
            rSrc.top    = 0; 
            rSrc.right  = 0; 
            rSrc.bottom = 0; 
        } 
 
        // Get Primary Rectangle 
        ZeroMemory (&ddsd, sizeof(ddsd)); 
        ddsd.dwSize = sizeof (DDSURFACEDESC); 
        hResult = lpddsPrimary->GetSurfaceDesc (&ddsd); 
        if (FAILED (hResult)) 
        { 
            // Error 
            return hResult; 
        } 
 
        rPrim.left      = 0; 
        rPrim.top       = 0; 
        rPrim.right     = rPrim.left + ddsd.dwWidth; 
        rPrim.bottom    = rPrim.top + ddsd.dwHeight; 
 
        // Map src rectangle in surface coordinates to  
// primary surface coordinates 
        pnt.x = rSrc.left; 
        pnt.y = rSrc.top; 
        ClientToScreen (hWindow, &pnt); 
 
        rTemp.left   = pnt.x; 
        rTemp.top    = pnt.y; 
        rTemp.right  = rTemp.left + (rSrc.right - rSrc.left); 
        rTemp.bottom = rTemp.top + (rSrc.bottom - rSrc.top); 
 
        // Intersect temp with primary to get dest Rect 
        if (! IntersectRect (&rDest, &rPrim, &rTemp)) 
        { 
            // No intersection, means nothing to draw 
            turnPaintOff (); 
            rDest.left   = 0; 
            rDest.top    = 0; 
            rDest.right  = 0; 
            rDest.bottom = 0; 
        } 
 
        // Now we have to map the dest rectangle back into 
        // surface space to to get the final src rectangle 
        rSrc.left   = rSrc.left + (rDest.left - pnt.x); 
        rSrc.top    = rSrc.top + (rDest.top - pnt.y); 
        rSrc.right  = rSrc.left + (rDest.right - rDest.left); 
        rSrc.bottom = rSrc.top + (rDest.bottom - rDest.top); 
 
        // Save src and dest rectangles 
        rDrawDest.left   = rDest.left; 
        rDrawDest.top    = rDest.top; 
        rDrawDest.right  = rDest.right; 
        rDrawDest.bottom = rDest.bottom; 
 
        rDrawSrc.left   = rSrc.left; 
        rDrawSrc.top    = rSrc.top; 
        rDrawSrc.right  = rSrc.right; 
        rDrawSrc.bottom = rSrc.bottom; 
 
        // No longer need to calculate rects 
        turnCalcRectsOff (); 
    } 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::CalcRects 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::Restore 
** Purpose: Restores lost surfaces 
** Note:    Eventually we should inform the user somehow that 
**          they need to redraw the surface but for now punt 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::Restore (void) 
{ 
    HRESULT hResult; 
 
// Check Initialization 
    if (! isValid ()) 
        return DDERR_GENERIC; 
 
    // Restore Primary Surface 
    if (lpddsPrimary) 
    { 
        hResult = lpddsPrimary->Restore (); 
        if (FAILED (hResult)) 
            return hResult; 
    } 
 
    // Restore Z Buffer 
    if (lpddsZBuff) 
    { 
        hResult = lpddsZBuff->Restore (); 
        if (FAILED (hResult)) 
            return hResult; 
    } 
 
    // Restore Rendering surface 
    if (lpddsRender) 
    { 
        hResult = lpddsRender->Restore (); 
        if (FAILED (hResult)) 
            return hResult; 
    } 
 
// Allow D3D Scene to restore any surfaces 
if (lpd3dScene) 
{ 
hResult = lpd3dScene->Restore (); 
if (FAILED (hResult)) 
return hResult; 
} 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::Restore 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::GetSurfaceRect 
** Purpose: Get bounding rectangle of surface 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::GetSurfaceRect (RECT & rSurface) 
{ 
    HRESULT hResult; 
 
    if (! isValid ()) 
    { 
        // Error 
        return DDERR_GENERIC; 
    } 
 
    // Force update of rectangle coordinates 
    hResult = CalcRects (); 
    if (FAILED (hResult)) 
        return hResult; 
 
    // Return Surface rectangle 
    rSurface = rSurf; 
 
    // Success 
    return DD_OK; 
} // D3DWindow::GetSurfaceRect 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::GetPrimaryRect 
** Purpose: Get bounding rectangle of primary surface 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::GetPrimaryRect (RECT & rPrimary) 
{ 
    HRESULT hResult; 
 
    if (! isValid ()) 
    { 
        // Error 
        return DDERR_GENERIC; 
    } 
 
    // Force update of rectangle coordinates 
    hResult = CalcRects (); 
    if (FAILED (hResult)) 
    { 
        // Error 
        REPORTERR (hResult); 
        return hResult; 
    } 
 
    // Return Primary rectangle 
    rPrimary = rPrim; 
 
    // Success 
    return DD_OK; 
} // GetPrimaryRect 
   
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::GetSrcRect 
** Purpose: Get current Src Rectangle 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::GetSrcRect (RECT & rSrc) 
{ 
    HRESULT hResult; 
 
    if (! isValid ()) 
    { 
        // Error 
        return DDERR_GENERIC; 
    } 
 
    // Force update of rectangle coordinates 
    hResult = CalcRects (); 
    if (FAILED (hResult)) 
        return hResult; 
 
    // Return Surface rectangle 
    rSrc = rDrawSrc; 
 
    // Success 
    return DD_OK; 
} // D3DWindow::GetSrcRect 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::GetDestRect 
** Purpose: Get current Dest Rectangle 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::GetDestRect (RECT & rDest) 
{ 
    HRESULT hResult; 
 
    if (! isValid ()) 
    { 
        // Error 
        return DDERR_GENERIC; 
    } 
 
    // Force update of rectangle coordinates 
    hResult = CalcRects (); 
    if (FAILED (hResult)) 
        return hResult; 
 
    // Return Surface rectangle 
    rDest = rDrawDest; 
 
    // Success 
    return DD_OK; 
} // D3DWindow::GetDestRect 
   
 
/* 
**----------------------------------------------------------------------------- 
**  Name:       D3DWindow::ChangeDesktop 
**  Purpose:    The Primary Desktop has changed Modes, so we need 
**to update our current mode to stay in sync 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::ChangeDesktop (void) 
{ 
HRESULT hResult; 
LPDDDrvInfo  lpDrvOld; 
LPDDModeInfo lpModeOld, lpModeNew; 
LPD3DDevInfo lpDevOld, lpDevNew; 
LPGUID lpGuidD3D; 
 
// Check Initialization 
if ((! hWindow) || (! IsWindow (hWindow))) 
{ 
REPORTERR (DDERR_INVALIDPARAMS); 
return DDERR_INVALIDPARAMS; 
} 
 
lpDrvOld  = lpCurrDriver; 
lpModeOld = lpCurrMode; 
lpDevOld  = lpCurrDevice; 
 
if (! lpDrvOld) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
 
// 
// Step 1. Get New Mode (and new device) 
// 
 
// Get D3D Guid 
if (lpDevOld) 
lpGuidD3D = &(lpDevOld->guid); 
else 
lpGuidD3D = NULL; 
 
// Get New Mode and Device corresponding to current desktop 
if (! GetDesktopMode (lpDrvOld, lpGuidD3D, &lpModeNew, &lpDevNew)) 
{ 
REPORTERR (DDERR_GENERIC); 
return DDERR_GENERIC; 
} 
 
// Do they match 
if (lpModeOld == lpModeNew) 
{ 
// Nothing to do 
return DD_OK; 
} 
 
//  
// Step 2.Destroy current Mode 
// 
FiniRender (); 
FiniPrimary (); 
//  FiniMode ();// Don't do this => unnecessary mode switch 
 
// 
// Step 3.  Create new mode 
// 
lpCurrMode = lpModeNew; 
if (lpDevNew) 
lpCurrDevice = lpDevNew; 
 
// Create Primary Surface 
hResult = InitPrimary (); 
if (FAILED (hResult)) 
{ 
// Error, no point in trying to restore old defaults 
// they are out of sync with the current desktop 
return hResult; 
} 
 
 
// Create Render surface 
hResult = InitRender (); 
if (FAILED (hResult)) 
{ 
// Error, no point in trying to restore old defaults 
// they are out of sync with the current desktop 
return hResult; 
} 
 
// Notify the window of a successful change in Mode 
SendMessage (hWindow, D3DWIN_CHANGED_MODE, 0, 0); 
 
// Success 
    return DD_OK; 
} // D3DInfo::ChangeDesktop 
   
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::ChangeDriver 
** Purpose:  
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::ChangeDriver (LPGUID lpGuidDD, LPD3DDevInfo lpDevHint) 
{ 
HRESULT hResult; 
    LPGUID       lpGuidD3D; 
LPDDDrvInfo  lpDrvNew,  lpDrvOld; 
LPDDModeInfo lpModeNew, lpModeOld; 
LPD3DDevInfo lpDevNew, lpDevOld; 
 
// Get New Driver 
lpDrvNew = ValidateDriver (lpGuidDD); 
if (! lpDrvNew) 
{ 
return DDERR_GENERIC; 
} 
 
// Are we already using the requested driver 
if ((isValid ()) && (lpDrvNew == lpCurrDriver)) 
return DD_OK; 
 
// Get reasonable defaults for Mode and D3D device 
if (lpDevHint) 
lpGuidD3D = &(lpDevHint->guid); 
else if (lpCurrDevice) 
lpGuidD3D = &(lpCurrDevice->guid); 
else 
lpGuidD3D = NULL; 
 
if (! GetDesktopMode (lpDrvNew, lpGuidD3D, &lpModeNew, &lpDevNew)) 
{ 
return DDERR_GENERIC; 
} 
 
// Save old defaults 
lpDrvOld= lpCurrDriver; 
lpModeOld= lpCurrMode; 
lpDevOld= lpCurrDevice; 
 
// Destroy almost everything 
Fini (); 
 
// Set new defaults 
lpCurrDriver = lpDrvNew; 
lpCurrMode   = lpModeNew; 
lpCurrDevice = lpDevNew; 
 
// Re-create almost everything based on new driver 
hResult = Init (); 
if (FAILED (hResult)) 
{ 
// Try to restore old defaults 
Fini (); 
 
lpCurrDriver = lpDrvOld; 
lpCurrMode   = lpModeOld; 
lpCurrDevice = lpDevOld; 
 
Init (); 
return hResult; 
} 
 
// Notify the window of a successful change in Driver 
SendMessage (hWindow, D3DWIN_CHANGED_DRIVER, 0, 0); 
 
// Success 
    return DD_OK; 
} // End D3DWindow::ChangeDriver 
 
 
   
/* 
**----------------------------------------------------------------------------- 
** Name:    D3DWindow::ChangeDevice 
** Purpose: Change to a new D3D device (RAMP, RGB, Hardware, etc.) 
** Notes: 
** 
**  Algorithm: 
**- Destroy the current D3D Device (and associated surfaces) 
**- Recreate a new D3D device from the new GUID 
** 
**  1.The new D3D Device may not be supported by the current DD Device. 
** 
**  2.  The new D3D Device may not be compatible with the current Mode 
**- If we are windowed, then punt (we shouldn't change the desktop) 
**- If we are full-screen then pick a new mode that is compatible 
**- This means destroy the current mode and recreate it. 
** 
**----------------------------------------------------------------------------- 
*/ 
 
HRESULT D3DWindow::ChangeDevice (LPGUID lpD3DGuid) 
{ 
    HRESULT         hResult; 
LPDDDrvInfolpDrvOld; 
LPDDModeInfo    lpModeOld; 
LPD3DDevInfolpDevNew, lpDevOld; 
 
// Check Parameters 
if (! lpD3DGuid) 
{ 
REPORTERR (DDERR_INVALIDPARAMS); 
        return DDERR_INVALIDPARAMS; 
} 
 
// Check Initialization 
    if (! isValid () || (! lpddsRender)) 
{ 
REPORTERR (DDERR_GENERIC); 
        return DDERR_GENERIC; 
} 
 
// Save Original State 
lpDrvOld= lpCurrDriver; 
lpModeOld   = lpCurrMode; 
lpDevOld= lpCurrDevice; 
 
// Verify new D3D device belongs to current DD driver 
lpDevNew = lpDrvOld->FindDevice (lpD3DGuid, NULL); 
if (! lpDevNew) 
{ 
REPORTERR (DDERR_INVALIDPARAMS); 
        return DDERR_INVALIDPARAMS; 
} 
 
// 
//Step 1. Verify new D3D device is supported with current mode 
// 
if (! lpModeOld->ModeSupported (lpDevNew)) 
{ 
// We can't change the mode 
// as we shouldn't be changing the desktop 
REPORTERR (DDERR_INVALIDPARAMS); 
return DDERR_INVALIDPARAMS; 
} 
 
 
// 
//Step 2.  Destroy Old D3D Device 
// 
FiniRender (); 
 
// 
//Step 3. Create new D3D Device 
// 
 
// Set new D3D device (and mode) 
lpCurrDevice = lpDevNew; 
 
// Create new D3D Device 
hResult = InitRender (); 
if (FAILED (hResult)) 
{ 
// Try to restore original device 
lpCurrDevice = lpDevOld; 
InitRender (); 
 
// Return Error 
REPORTERR (hResult); 
return hResult; 
} 
 
// Notify the window of a successful change in device 
SendMessage (hWindow, D3DWIN_CHANGED_DEVICE, 0, 0); 
 
    // Success 
    return DD_OK; 
} // End D3DWindow::ChangeDevice 
 
 
 
/* 
**----------------------------------------------------------------------------- 
** End of File 
**----------------------------------------------------------------------------- 
*/