

#include "StdAfx.h"





#if 0
//-----------------------------------------------------------------------------
// File: D3DFrame.cpp
//
// Desc: Class functions to implement a Direct3D app framework.
//
// Copyright (c) 1995-1999 by Microsoft, all rights reserved
//-----------------------------------------------------------------------------
//#define STRICT
//#include <windows.h>
//#include <tchar.h>
#include "D3DFrame.h"
#include "D3DUtil.h"




//-----------------------------------------------------------------------------
// Name: CD3DFramework7()
// Desc: The constructor. Clears static variables
//-----------------------------------------------------------------------------
CD3DFramework7::CD3DFramework7()
{
     m_hWnd           = NULL;
     m_bIsFullscreen  = FALSE;
     m_bIsStereo      = FALSE;
     m_dwRenderWidth  = 0L;
     m_dwRenderHeight = 0L;
     
     m_pddsFrontBuffer    = NULL;
     m_pddsBackBuffer     = NULL;
     m_pddsBackBufferLeft = NULL;
     
     m_pddsZBuffer     = NULL;
     m_pd3dDevice      = NULL;
     m_pDD             = NULL;
     m_pD3D            = NULL;
     m_dwDeviceMemType = NULL;
}




//-----------------------------------------------------------------------------
// Name: ~CD3DFramework7()
// Desc: The destructor. Deletes all objects
//-----------------------------------------------------------------------------
CD3DFramework7::~CD3DFramework7()
{
    DestroyObjects();
}




//-----------------------------------------------------------------------------
// Name: DestroyObjects()
// Desc: Cleans everything up upon deletion. This code returns an error
//       if any of the objects have remaining reference counts.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::DestroyObjects()
{
    LONG nDD  = 0L; // Number of outstanding DDraw references
    LONG nD3D = 0L; // Number of outstanding D3DDevice references

    if (m_pDD)
    {
        m_pDD->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL);
    }

    // Do a safe check for releasing the D3DDEVICE. RefCount must be zero.
    if (m_pd3dDevice)
        if (0 < (nD3D = m_pd3dDevice->Release()))
            DEBUG_MSG(_T("Error: D3DDevice object is still referenced!"));
    m_pd3dDevice = NULL;

    SAFE_RELEASE(m_pddsBackBuffer);
    SAFE_RELEASE(m_pddsBackBufferLeft);
    SAFE_RELEASE(m_pddsZBuffer);
    SAFE_RELEASE(m_pddsFrontBuffer);
    SAFE_RELEASE(m_pD3D);

    if (m_pDD)
    {
        // Do a safe check for releasing DDRAW. RefCount must be zero.
        if (0 < (nDD = m_pDD->Release()))
            DEBUG_MSG(_T("Error: DDraw object is still referenced!"));
    }
    m_pDD = NULL;

    // Return successful, unless there are outstanding DD or D3DDevice refs.
    return (nDD==0 && nD3D==0) ? S_OK : D3DFWERR_NONZEROREFCOUNT;
}




//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Creates the internal objects for the framework
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::Initialize(HWND hWnd, GUID* pDriverGUID,
                                    GUID* pDeviceGUID, DDSURFACEDESC2* pMode,
                                    DWORD dwFlags)
{
    HRESULT hr;

    // Check params. Note: A NULL mode is valid for windowed modes only.
    if ((NULL==hWnd) || (NULL==pDeviceGUID) || 
        (NULL==pMode && (dwFlags&D3DFW_FULLSCREEN)))
        return E_INVALIDARG;

    // Setup state for windowed/fullscreen mode
    m_hWnd          = hWnd;
    m_bIsStereo     = FALSE;
    m_bIsFullscreen = (dwFlags & D3DFW_FULLSCREEN) ? TRUE : FALSE;

    // Support stereoscopic viewing for fullscreen modes which support it
    if ((dwFlags & D3DFW_STEREO) && (dwFlags & D3DFW_FULLSCREEN))
        if (pMode->ddsCaps.dwCaps2 & DDSCAPS2_STEREOSURFACELEFT)
            m_bIsStereo = TRUE;

    // Create the D3D rendering environment (surfaces, device, viewport, etc.)
    if (FAILED(hr = CreateEnvironment(pDriverGUID, pDeviceGUID, pMode,
                                        dwFlags)))
    {
        DestroyObjects();
        return hr;
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateEnvironment()
// Desc: Creates the internal objects for the framework
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateEnvironment(GUID* pDriverGUID, GUID* pDeviceGUID,
                                           DDSURFACEDESC2* pMode, DWORD dwFlags)
{
    HRESULT hr;

    // Select the default memory type, for whether the device is HW or SW
    if (IsEqualIID(*pDeviceGUID, IID_IDirect3DHALDevice))
        m_dwDeviceMemType = DDSCAPS_VIDEOMEMORY;
    else if (IsEqualIID(*pDeviceGUID, IID_IDirect3DTnLHalDevice))
        m_dwDeviceMemType = DDSCAPS_VIDEOMEMORY;
    else
        m_dwDeviceMemType = DDSCAPS_SYSTEMMEMORY;

    // Create the DDraw object
    hr = CreateDirectDraw(pDriverGUID, dwFlags);
    if (FAILED(hr))
        return hr;

    // Create the front and back buffers, and attach a clipper
    if (dwFlags & D3DFW_FULLSCREEN)
        hr = CreateFullscreenBuffers(pMode);
    else
        hr = CreateWindowedBuffers();
    if (FAILED(hr))
        return hr;

    // Create the Direct3D object and the Direct3DDevice object
    hr = CreateDirect3D(pDeviceGUID);
    if (FAILED(hr))
        return hr;

    // Create and attach the zbuffer
    if (dwFlags & D3DFW_ZBUFFER)
        hr = CreateZBuffer(pDeviceGUID);
    if (FAILED(hr))
        return hr;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: EnumZBufferFormatsCallback()
// Desc: Simply returns the first matching enumerated z-buffer format
//-----------------------------------------------------------------------------
static HRESULT WINAPI EnumZBufferFormatsCallback(DDPIXELFORMAT* pddpf,
                                                  VOID* pContext)
{
    DDPIXELFORMAT* pddpfOut = (DDPIXELFORMAT*)pContext;

    if (pddpfOut->dwRGBBitCount == pddpf->dwRGBBitCount)
    {
        (*pddpfOut) = (*pddpf);
        return D3DENUMRET_CANCEL;
    }

    return D3DENUMRET_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateDirectDraw()
// Desc: Create the DirectDraw interface
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateDirectDraw(GUID* pDriverGUID, DWORD dwFlags)
{
    // Create the DirectDraw interface, and query for the DD7 interface
    if (FAILED(DirectDrawCreateEx(pDriverGUID, (VOID**)&m_pDD,
                                    IID_IDirectDraw7, NULL)))
    {
        DEBUG_MSG(_T("Could not create DirectDraw"));
        return D3DFWERR_NODIRECTDRAW;
    }

    // Set the Windows cooperative level
    DWORD dwCoopFlags = DDSCL_NORMAL;
    if (m_bIsFullscreen)
        dwCoopFlags = DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN;

    // By defualt, set the flag to allow D3D to optimize floating point calcs
    if (0L == (dwFlags & D3DFW_NO_FPUSETUP))
        dwCoopFlags |= DDSCL_FPUSETUP;

    if (FAILED(m_pDD->SetCooperativeLevel(m_hWnd, dwCoopFlags)))
    {
        DEBUG_MSG(_T("Couldn't set coop level"));
        return D3DFWERR_COULDNTSETCOOPLEVEL;
    }

    // Check that we are NOT in a palettized display. That case will fail,
    // since the framework doesn't use palettes.
    DDSURFACEDESC2 ddsd;
    ddsd.dwSize = sizeof(ddsd);
    m_pDD->GetDisplayMode(&ddsd);
    if (ddsd.ddpfPixelFormat.dwRGBBitCount <= 8)
        return D3DFWERR_INVALIDMODE;

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateFullscreenBuffers()
// Desc: Creates the primary and (optional) backbuffer for rendering.
//       Windowed mode and fullscreen mode are handled differently.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateFullscreenBuffers(DDSURFACEDESC2* pddsd)
{
    HRESULT hr;

    // Get the dimensions of the screen bounds
    // Store the rectangle which contains the renderer
    SetRect(&m_rcScreenRect, 0, 0, pddsd->dwWidth, pddsd->dwHeight);
    m_dwRenderWidth  = m_rcScreenRect.right  - m_rcScreenRect.left;
    m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top;

    // Set the display mode to the requested dimensions. Check for
    // 320x200x8 modes, and set flag to avoid using ModeX
    DWORD dwModeFlags = 0;

    if ((320==m_dwRenderWidth) && (200==m_dwRenderHeight) &&
        (8==pddsd->ddpfPixelFormat.dwRGBBitCount))
        dwModeFlags |= DDSDM_STANDARDVGAMODE;

    if (FAILED(m_pDD->SetDisplayMode(m_dwRenderWidth, m_dwRenderHeight,
                                pddsd->ddpfPixelFormat.dwRGBBitCount,
                                pddsd->dwRefreshRate, dwModeFlags)))
    {
        DEBUG_MSG(_T("Can't set display mode"));
        return D3DFWERR_BADDISPLAYMODE;
    }

    // Setup to create the primary surface w/backbuffer
    DDSURFACEDESC2 ddsd;
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize            = sizeof(ddsd);
    ddsd.dwFlags           = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps    = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE |
                             DDSCAPS_FLIP | DDSCAPS_COMPLEX;
    ddsd.dwBackBufferCount = 1;

    // Support for stereoscopic viewing
    if (m_bIsStereo)
    {
        ddsd.ddsCaps.dwCaps  |= DDSCAPS_VIDEOMEMORY;
        ddsd.ddsCaps.dwCaps2 |= DDSCAPS2_STEREOSURFACELEFT;
    }

    // Create the primary surface
    if (FAILED(hr = m_pDD->CreateSurface(&ddsd, &m_pddsFrontBuffer, NULL)))
    {
        DEBUG_MSG(_T("Error: Can't create primary surface"));
        if (hr != DDERR_OUTOFVIDEOMEMORY)
            return D3DFWERR_NOPRIMARY;
        DEBUG_MSG(_T("Error: Out of video memory"));
        return DDERR_OUTOFVIDEOMEMORY;
    }

    // Get the backbuffer, which was created along with the primary.
    DDSCAPS2 ddscaps = { DDSCAPS_BACKBUFFER, 0, 0, 0 };
    if (FAILED(hr = m_pddsFrontBuffer->GetAttachedSurface(&ddscaps,
                                                &m_pddsBackBuffer)))
    {
        DEBUG_ERR(hr, _T("Error: Can't get the backbuffer"));
        return D3DFWERR_NOBACKBUFFER;
    }

    // Increment the backbuffer count (for consistency with windowed mode)
    m_pddsBackBuffer->AddRef();

    // Support for stereoscopic viewing
    if (m_bIsStereo)
    {
        // Get the left backbuffer, which was created along with the primary.
        DDSCAPS2 ddscaps = { 0, DDSCAPS2_STEREOSURFACELEFT, 0, 0 };
        if (FAILED(hr = m_pddsBackBuffer->GetAttachedSurface(&ddscaps,
                                                    &m_pddsBackBufferLeft)))
        {
            DEBUG_ERR(hr, _T("Error: Can't get the left backbuffer"));
            return D3DFWERR_NOBACKBUFFER;
        }
        m_pddsBackBufferLeft->AddRef();
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateWindowedBuffers()
// Desc: Creates the primary and (optional) backbuffer for rendering.
//       Windowed mode and fullscreen mode are handled differently.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateWindowedBuffers()
{
    HRESULT hr;

    // Get the dimensions of the viewport and screen bounds
    GetClientRect(m_hWnd, &m_rcScreenRect);
    ClientToScreen(m_hWnd, (POINT*)&m_rcScreenRect.left);
    ClientToScreen(m_hWnd, (POINT*)&m_rcScreenRect.right);
    m_dwRenderWidth  = m_rcScreenRect.right  - m_rcScreenRect.left;
    m_dwRenderHeight = m_rcScreenRect.bottom - m_rcScreenRect.top;

    // Create the primary surface
    DDSURFACEDESC2 ddsd;
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize         = sizeof(ddsd);
    ddsd.dwFlags        = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    if (FAILED(hr = m_pDD->CreateSurface(&ddsd, &m_pddsFrontBuffer, NULL)))
    {
        DEBUG_MSG(_T("Error: Can't create primary surface"));
        if (hr != DDERR_OUTOFVIDEOMEMORY)
            return D3DFWERR_NOPRIMARY;
        DEBUG_MSG(_T("Error: Out of video memory"));
        return DDERR_OUTOFVIDEOMEMORY;
    }

    // If in windowed-mode, create a clipper object
    LPDIRECTDRAWCLIPPER pcClipper;
    if (FAILED(hr = m_pDD->CreateClipper(0, &pcClipper, NULL)))
    {
        DEBUG_MSG(_T("Error: Couldn't create clipper"));
        return D3DFWERR_NOCLIPPER;
    }

    // Associate the clipper with the window
    pcClipper->SetHWnd(0, m_hWnd);
    m_pddsFrontBuffer->SetClipper(pcClipper);
    SAFE_RELEASE(pcClipper);

    // Create a backbuffer
    ddsd.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
    ddsd.dwWidth        = m_dwRenderWidth;
    ddsd.dwHeight       = m_dwRenderHeight;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;

    if (FAILED(hr = m_pDD->CreateSurface(&ddsd, &m_pddsBackBuffer, NULL)))
    {
        DEBUG_ERR(hr, _T("Error: Couldn't create the backbuffer"));
        if (hr != DDERR_OUTOFVIDEOMEMORY)
            return D3DFWERR_NOBACKBUFFER;
        DEBUG_MSG(_T("Error: Out of video memory"));
        return DDERR_OUTOFVIDEOMEMORY;
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateDirect3D()
// Desc: Create the Direct3D interface
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateDirect3D(GUID* pDeviceGUID)
{
    // Query DirectDraw for access to Direct3D
    if (FAILED(m_pDD->QueryInterface(IID_IDirect3D7, (VOID**)&m_pD3D)))
    {
        DEBUG_MSG(_T("Couldn't get the Direct3D interface"));
        return D3DFWERR_NODIRECT3D;
    }

    // Create the device
    if (FAILED(m_pD3D->CreateDevice(*pDeviceGUID, m_pddsBackBuffer,
                                      &m_pd3dDevice)))
    {
        DEBUG_MSG(_T("Couldn't create the D3DDevice"));
        return D3DFWERR_NO3DDEVICE;
    }

    // Finally, set the viewport for the newly created device
    D3DVIEWPORT7 vp = { 0, 0, m_dwRenderWidth, m_dwRenderHeight, 0.0f, 1.0f };

    if (FAILED(m_pd3dDevice->SetViewport(&vp)))
    {
        DEBUG_MSG(_T("Error: Couldn't set current viewport to device"));
        return D3DFWERR_NOVIEWPORT;
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CreateZBuffer()
// Desc: Internal function called by Create() to make and attach a zbuffer
//       to the renderer
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::CreateZBuffer(GUID* pDeviceGUID)
{
    HRESULT hr;

    // Check if the device supports z-bufferless hidden surface removal. If so,
    // we don't really need a z-buffer
    D3DDEVICEDESC7 ddDesc;
    m_pd3dDevice->GetCaps(&ddDesc);
    if (ddDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR)
        return S_OK;

    // Get z-buffer dimensions from the render target
    DDSURFACEDESC2 ddsd;
    ddsd.dwSize = sizeof(ddsd);
    m_pddsBackBuffer->GetSurfaceDesc(&ddsd);

    // Setup the surface desc for the z-buffer.
    ddsd.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | m_dwDeviceMemType;
    ddsd.ddpfPixelFormat.dwSize = 0;  // Tag the pixel format as unitialized

    // Get an appropiate pixel format from enumeration of the formats. On the
    // first pass, we look for a zbuffer dpeth which is equal to the frame
    // buffer depth (as some cards unfornately require this).
    m_pD3D->EnumZBufferFormats(*pDeviceGUID, EnumZBufferFormatsCallback,
                                (VOID*)&ddsd.ddpfPixelFormat);
    if (0 == ddsd.ddpfPixelFormat.dwSize)
    {
        // Try again, just accepting any 16-bit zbuffer
        ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
        m_pD3D->EnumZBufferFormats(*pDeviceGUID, EnumZBufferFormatsCallback,
                                    (VOID*)&ddsd.ddpfPixelFormat);
            
        if (0 == ddsd.ddpfPixelFormat.dwSize)
        {
            DEBUG_MSG(_T("Device doesn't support requested zbuffer format"));
            return D3DFWERR_NOZBUFFER;
        }
    }

    // Create and attach a z-buffer
    if (FAILED(hr = m_pDD->CreateSurface(&ddsd, &m_pddsZBuffer, NULL)))
    {
        DEBUG_MSG(_T("Error: Couldn't create a ZBuffer surface"));
        if (hr != DDERR_OUTOFVIDEOMEMORY)
            return D3DFWERR_NOZBUFFER;
        DEBUG_MSG(_T("Error: Out of video memory"));
        return DDERR_OUTOFVIDEOMEMORY;
    }

    if (FAILED(m_pddsBackBuffer->AddAttachedSurface(m_pddsZBuffer)))
    {
        DEBUG_MSG(_T("Error: Couldn't attach zbuffer to render surface"));
        return D3DFWERR_NOZBUFFER;
    }

    // For stereoscopic viewing, attach zbuffer to left surface as well
    if (m_bIsStereo)
    {
        if (FAILED(m_pddsBackBufferLeft->AddAttachedSurface(m_pddsZBuffer)))
        {
            DEBUG_MSG(_T("Error: Couldn't attach zbuffer to left render surface"));
            return D3DFWERR_NOZBUFFER;
        }
    }

    // Finally, this call rebuilds internal structures
    if (FAILED(m_pd3dDevice->SetRenderTarget(m_pddsBackBuffer, 0L)))
    {
        DEBUG_MSG(_T("Error: SetRenderTarget() failed after attaching zbuffer!"));
        return D3DFWERR_NOZBUFFER;
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: RestoreSurfaces()
// Desc: Checks for lost surfaces and restores them if lost. Note: Don't
//       restore render surface, since it's just a duplicate ptr.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::RestoreSurfaces()
{
    // Restore all surfaces (including video memory vertex buffers)
    m_pDD->RestoreAllSurfaces();

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: Move()
// Desc: Moves the screen rect for windowed renderers
//-----------------------------------------------------------------------------
VOID CD3DFramework7::Move(INT x, INT y)
{
    if (TRUE == m_bIsFullscreen)
        return;

    SetRect(&m_rcScreenRect, x, y, x + m_dwRenderWidth, y + m_dwRenderHeight);
}




//-----------------------------------------------------------------------------
// Name: FlipToGDISurface()
// Desc: Puts the GDI surface in front of the primary, so that dialog
//       boxes and other windows drawing funcs may happen.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::FlipToGDISurface(BOOL bDrawFrame)
{
    if (m_pDD && m_bIsFullscreen)
    {
        m_pDD->FlipToGDISurface();

        if (bDrawFrame)
        {
            DrawMenuBar(m_hWnd);
            RedrawWindow(m_hWnd, NULL, NULL, RDW_FRAME);
        }
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: ShowFrame()
// Desc: Show the frame on the primary surface, via a blt or a flip.
//-----------------------------------------------------------------------------
HRESULT CD3DFramework7::ShowFrame()
{
    if (NULL == m_pddsFrontBuffer)
        return D3DFWERR_NOTINITIALIZED;

    if (m_bIsFullscreen)
    {
        // We are in fullscreen mode, so perform a flip.
        if (m_bIsStereo)
            return m_pddsFrontBuffer->Flip(NULL, DDFLIP_WAIT | DDFLIP_STEREO);
        else
            return m_pddsFrontBuffer->Flip(NULL, DDFLIP_WAIT);
    }
    else
    {
        // We are in windowed mode, so perform a blit.
        return m_pddsFrontBuffer->Blt(&m_rcScreenRect, m_pddsBackBuffer,
                                       NULL, DDBLT_WAIT, NULL);
    }
}



#endif
