Bagikan melalui


Panduan: Membuat Konten Direct3D9 untuk Hosting di WPF

Panduan ini menunjukkan cara membuat konten Direct3D9 yang cocok untuk hosting di aplikasi Windows Presentation Foundation (WPF). Untuk informasi selengkapnya tentang menghosting konten Direct3D9 di aplikasi WPF, lihat Interoperasi WPF dan Direct3D9.

Dalam panduan ini, Anda melakukan tugas-tugas berikut:

  • Buat proyek Direct3D9.

  • Konfigurasikan proyek Direct3D9 untuk hosting dalam aplikasi WPF.

Setelah selesai, Anda akan memiliki DLL yang berisi konten Direct3D9 untuk digunakan dalam aplikasi WPF.

Prasyarat

Anda memerlukan komponen berikut untuk menyelesaikan panduan ini:

  • Visual Studio 2010.

  • DirectX SDK 9 atau yang lebih baru.

Membuat Proyek Direct3D9

Langkah pertama adalah membuat dan mengonfigurasi proyek Direct3D9.

Untuk membuat proyek Direct3D9

  1. Buat Proyek Win32 baru di C++ bernama D3DContent.

    Panduan Aplikasi Win32 terbuka dan menampilkan layar Selamat Datang.

  2. Klik Berikutnya.

    Layar Aplikasi Pengaturan muncul.

  3. Di bagian Jenis aplikasi : , pilih opsi DLL .

  4. Klik Selesai.

    Proyek D3DContent dihasilkan.

  5. Di Penjelajah Solusi, klik kanan proyek D3DContent dan pilih Properti.

    Kotak dialog Halaman Properti D3DContent terbuka.

  6. Pilih simpul C/C++ .

  7. Di bidang Sertakan Direktori Tambahan, tentukan lokasi folder Sertakan DirectX. Lokasi default untuk folder ini adalah %ProgramFiles%\Microsoft DirectX SDK (versi)\Include.

  8. Klik dua kali simpul Linker untuk memperluasnya.

  9. Di bidang Direktori Pustaka Tambahan, tentukan lokasi folder pustaka DirectX. Lokasi default untuk folder ini adalah %ProgramFiles%\Microsoft DirectX SDK (versi)\Lib\x86.

  10. Pilih simpul Input .

  11. Di bidang Dependensi Tambahan, tambahkan d3d9.lib file dan d3dx9.lib .

  12. Di Penjelajah Solusi, tambahkan file definisi modul baru (.def) bernama D3DContent.def ke proyek.

Membuat Konten Direct3D9

Untuk mendapatkan performa terbaik, konten Direct3D9 Anda harus menggunakan pengaturan tertentu. Kode berikut menunjukkan cara membuat permukaan Direct3D9 yang memiliki karakteristik performa terbaik. Untuk informasi selengkapnya, lihat Pertimbangan Performa untuk Interoperabilitas Direct3D9 dan WPF.

Untuk membuat konten Direct3D9

  1. Dengan menggunakan Penjelajah Solusi, tambahkan tiga kelas C++ ke proyek bernama berikut ini.

    CRenderer (dengan destruktor virtual)

    CRendererManager

    CTriangleRenderer

  2. Buka Renderer.h di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    
    #pragma once
    
    class CRenderer
    {
    public:
        virtual ~CRenderer();
    
        HRESULT CheckDeviceState();
        HRESULT CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples);
    
        virtual HRESULT Render() = 0;
    
        IDirect3DSurface9 *GetSurfaceNoRef() { return m_pd3dRTS; }
    
    protected:
        CRenderer();
    
        virtual HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter);
    
        IDirect3DDevice9   *m_pd3dDevice;
        IDirect3DDevice9Ex *m_pd3dDeviceEx;
    
        IDirect3DSurface9 *m_pd3dRTS;
    
    };
    
  3. Buka Renderer.cpp di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    //+-----------------------------------------------------------------------------
    //
    //  CRenderer
    //
    //      An abstract base class that creates a device and a target render 
    //      surface. Derive from this class and override Init() and Render() 
    //      to do your own rendering. See CTriangleRenderer for an example.
    
    
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer ctor
    //
    //------------------------------------------------------------------------------
    CRenderer::CRenderer() : m_pd3dDevice(NULL), m_pd3dDeviceEx(NULL), m_pd3dRTS(NULL)
    {
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer dtor
    //
    //------------------------------------------------------------------------------
    CRenderer::~CRenderer()
    {
        SAFE_RELEASE(m_pd3dDevice);
        SAFE_RELEASE(m_pd3dDeviceEx);
        SAFE_RELEASE(m_pd3dRTS);
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::CheckDeviceState
    //
    //  Synopsis:
    //      Returns the status of the device. 9Ex devices are a special case because 
    //      TestCooperativeLevel() has been deprecated in 9Ex.
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRenderer::CheckDeviceState()
    {
        if (m_pd3dDeviceEx)
        {
            return m_pd3dDeviceEx->CheckDeviceState(NULL);
        }
        else if (m_pd3dDevice)
        {
            return m_pd3dDevice->TestCooperativeLevel();
        }
        else
        {
            return D3DERR_DEVICELOST;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::CreateSurface
    //
    //  Synopsis:
    //      Creates and sets the render target
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
    {
        HRESULT hr = S_OK;
    
        SAFE_RELEASE(m_pd3dRTS);
    
        IFC(m_pd3dDevice->CreateRenderTarget(
            uWidth,
            uHeight,
            fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
            static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
            0,
            m_pd3dDeviceEx ? FALSE : TRUE,  // Lockable RT required for good XP perf
            &m_pd3dRTS,
            NULL
            ));
    
        IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::Init
    //
    //  Synopsis:
    //      Creates the device
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
    {
        HRESULT hr = S_OK;
    
        D3DPRESENT_PARAMETERS d3dpp;
        ZeroMemory(&d3dpp, sizeof(d3dpp));
        d3dpp.Windowed = TRUE;
        d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
        d3dpp.BackBufferHeight = 1;
        d3dpp.BackBufferWidth = 1;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    
        D3DCAPS9 caps;
        DWORD dwVertexProcessing;
        IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
        if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
        {
            dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
        }
        else
        {
            dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        }
    
        if (pD3DEx)
        {
            IDirect3DDevice9Ex *pd3dDevice = NULL;
            IFC(pD3DEx->CreateDeviceEx(
                uAdapter,
                D3DDEVTYPE_HAL,
                hwnd,
                dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
                &d3dpp,
                NULL,
                &m_pd3dDeviceEx
                ));
    
            IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));  
        }
        else 
        {
            assert(pD3D);
    
            IFC(pD3D->CreateDevice(
                uAdapter,
                D3DDEVTYPE_HAL,
                hwnd,
                dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
                &d3dpp,
                &m_pd3dDevice
                ));
        }
    
    Cleanup:
        return hr;
    }
    
  4. Buka RendererManager.h di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    
    #pragma once
    
    class CRenderer;
    
    class CRendererManager
    {
    public:
        static HRESULT Create(CRendererManager **ppManager);
        ~CRendererManager();
    
        HRESULT EnsureDevices();
    
        void SetSize(UINT uWidth, UINT uHeight);
        void SetAlpha(bool fUseAlpha);
        void SetNumDesiredSamples(UINT uNumSamples);
        void SetAdapter(POINT screenSpacePoint);
    
        HRESULT GetBackBufferNoRef(IDirect3DSurface9 **ppSurface);
    
        HRESULT Render();
    
    private:
        CRendererManager();
    
        void CleanupInvalidDevices();
        HRESULT EnsureRenderers();
        HRESULT EnsureHWND();
        HRESULT EnsureD3DObjects();
        HRESULT TestSurfaceSettings();
        void DestroyResources();
    
        IDirect3D9    *m_pD3D;
        IDirect3D9Ex  *m_pD3DEx;
    
        UINT m_cAdapters;
        CRenderer **m_rgRenderers;
        CRenderer *m_pCurrentRenderer;
    
        HWND m_hwnd;
    
        UINT m_uWidth;
        UINT m_uHeight;
        UINT m_uNumSamples;
        bool m_fUseAlpha;
        bool m_fSurfaceSettingsChanged;
    };
    
  5. Buka RendererManager.cpp di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    
    //+-----------------------------------------------------------------------------
    //
    //  CRendererManager
    //
    //      Manages the list of CRenderers. Managed code pinvokes into this class
    //      and this class forwards to the appropriate CRenderer.
    //
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    const static TCHAR szAppName[] = TEXT("D3DImageSample");
    typedef HRESULT (WINAPI *DIRECT3DCREATE9EXFUNCTION)(UINT SDKVersion, IDirect3D9Ex**);
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager ctor
    //
    //------------------------------------------------------------------------------
    CRendererManager::CRendererManager() 
        : 
        m_pD3D(NULL),
        m_pD3DEx(NULL),
        m_cAdapters(0),
        m_hwnd(NULL),
        m_pCurrentRenderer(NULL),
        m_rgRenderers(NULL),
        m_uWidth(1024),
        m_uHeight(1024),
        m_uNumSamples(0),
        m_fUseAlpha(false),
        m_fSurfaceSettingsChanged(true)
    {
    
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager dtor
    //
    //------------------------------------------------------------------------------
    CRendererManager::~CRendererManager()
    {
        DestroyResources();
    
        if (m_hwnd)
        {
            DestroyWindow(m_hwnd);
            UnregisterClass(szAppName, NULL);
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::Create
    //
    //  Synopsis:
    //      Creates the manager
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::Create(CRendererManager **ppManager)
    {
        HRESULT hr = S_OK;
    
        *ppManager = new CRendererManager();
        IFCOOM(*ppManager);
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureRenderers
    //
    //  Synopsis:
    //      Makes sure the CRenderer objects exist
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureRenderers()
    {
        HRESULT hr = S_OK;
    
        if (!m_rgRenderers)
        {
            IFC(EnsureHWND());
    
            assert(m_cAdapters);    
            m_rgRenderers = new CRenderer*[m_cAdapters];
            IFCOOM(m_rgRenderers);
            ZeroMemory(m_rgRenderers, m_cAdapters * sizeof(m_rgRenderers[0]));
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                IFC(CTriangleRenderer::Create(m_pD3D, m_pD3DEx, m_hwnd, i, &m_rgRenderers[i]));
            }
    
            // Default to the default adapter 
            m_pCurrentRenderer = m_rgRenderers[0];
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureHWND
    //
    //  Synopsis:
    //      Makes sure an HWND exists if we need it
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureHWND()
    {
        HRESULT hr = S_OK;
    
        if (!m_hwnd)
        {
            WNDCLASS wndclass;
    
            wndclass.style = CS_HREDRAW | CS_VREDRAW;
            wndclass.lpfnWndProc = DefWindowProc;
            wndclass.cbClsExtra = 0;
            wndclass.cbWndExtra = 0;
            wndclass.hInstance = NULL;
            wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
            wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
            wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
            wndclass.lpszMenuName = NULL;
            wndclass.lpszClassName = szAppName;
    
            if (!RegisterClass(&wndclass))
            {
                IFC(E_FAIL);
            }
    
            m_hwnd = CreateWindow(szAppName,
                                TEXT("D3DImageSample"),
                                WS_OVERLAPPEDWINDOW,
                                0,                   // Initial X
                                0,                   // Initial Y
                                0,                   // Width
                                0,                   // Height
                                NULL,
                                NULL,
                                NULL,
                                NULL);
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureD3DObjects
    //
    //  Synopsis:
    //      Makes sure the D3D objects exist
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureD3DObjects()
    {
        HRESULT hr = S_OK;
    
        HMODULE hD3D = NULL;
        if (!m_pD3D)
        {
            hD3D = LoadLibrary(TEXT("d3d9.dll"));
            DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
            if (pfnCreate9Ex)
            {
                IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
                IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
            }
            else
            {
                m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
                if (!m_pD3D) 
                {
                    IFC(E_FAIL);
                }
            }
    
            m_cAdapters = m_pD3D->GetAdapterCount();
        }
    
    Cleanup:
        if (hD3D)
        {
            FreeLibrary(hD3D);
        }
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::CleanupInvalidDevices
    //
    //  Synopsis:
    //      Checks to see if any devices are bad and if so, deletes all resources
    //
    //      We could delete resources and wait for D3DERR_DEVICENOTRESET and reset
    //      the devices, but if the device is lost because of an adapter order 
    //      change then our existing D3D objects would have stale adapter 
    //      information. We'll delete everything to be safe rather than sorry.
    //
    //------------------------------------------------------------------------------
    void
    CRendererManager::CleanupInvalidDevices()
    {
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            if (FAILED(m_rgRenderers[i]->CheckDeviceState()))
            {
                DestroyResources();
                break;
            }
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::GetBackBufferNoRef
    //
    //  Synopsis:
    //      Returns the surface of the current renderer without adding a reference
    //
    //      This can return NULL if we're in a bad device state.
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRendererManager::GetBackBufferNoRef(IDirect3DSurface9 **ppSurface)
    {
        HRESULT hr = S_OK;
    
        // Make sure we at least return NULL
        *ppSurface = NULL;
    
        CleanupInvalidDevices();
    
        IFC(EnsureD3DObjects());
    
        //
        // Even if we never render to another adapter, this sample creates devices
        // and resources on each one. This is a potential waste of video memory,
        // but it guarantees that we won't have any problems (e.g. out of video
        // memory) when switching to render on another adapter. In your own code
        // you may choose to delay creation but you'll need to handle the issues
        // that come with it.
        //
    
        IFC(EnsureRenderers());
    
        if (m_fSurfaceSettingsChanged)
        {
            if (FAILED(TestSurfaceSettings()))
            {
                IFC(E_FAIL);
            }
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                IFC(m_rgRenderers[i]->CreateSurface(m_uWidth, m_uHeight, m_fUseAlpha, m_uNumSamples));
            }
    
            m_fSurfaceSettingsChanged = false;
        }
    
        if (m_pCurrentRenderer)
        {
            *ppSurface = m_pCurrentRenderer->GetSurfaceNoRef();
        }
    
    Cleanup:
        // If we failed because of a bad device, ignore the failure for now and 
        // we'll clean up and try again next time.
        if (hr == D3DERR_DEVICELOST)
        {
            hr = S_OK;
        }
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::TestSurfaceSettings
    //
    //  Synopsis:
    //      Checks to see if our current surface settings are allowed on all
    //      adapters.
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::TestSurfaceSettings()
    {
        HRESULT hr = S_OK;
    
        D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
    
        // 
        // We test all adapters because because we potentially use all adapters.
        // But even if this sample only rendered to the default adapter, you
        // should check all adapters because WPF may move your surface to
        // another adapter for you!
        //
    
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            // Can we get HW rendering?
            IFC(m_pD3D->CheckDeviceType(
                i,
                D3DDEVTYPE_HAL,
                D3DFMT_X8R8G8B8,
                fmt,
                TRUE
                )); 
    
            // Is the format okay?
            IFC(m_pD3D->CheckDeviceFormat(
                i,
                D3DDEVTYPE_HAL,
                D3DFMT_X8R8G8B8,
                D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
                D3DRTYPE_SURFACE,
                fmt
                ));
    
            // D3DImage only allows multisampling on 9Ex devices. If we can't 
            // multisample, overwrite the desired number of samples with 0.
            if (m_pD3DEx && m_uNumSamples > 1)
            {   
                assert(m_uNumSamples <= 16);
    
                if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
                    i,
                    D3DDEVTYPE_HAL,
                    fmt,
                    TRUE,
                    static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
                    NULL
                    )))
                {
                    m_uNumSamples = 0;
                }
            }
            else
            {
                m_uNumSamples = 0;
            }
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::DestroyResources
    //
    //  Synopsis:
    //      Delete all D3D resources
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::DestroyResources()
    {
        SAFE_RELEASE(m_pD3D);
        SAFE_RELEASE(m_pD3DEx);
    
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            delete m_rgRenderers[i];
        }
        delete [] m_rgRenderers;
        m_rgRenderers = NULL;
    
        m_pCurrentRenderer = NULL;
        m_cAdapters = 0;
    
        m_fSurfaceSettingsChanged = true;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetSize
    //
    //  Synopsis:
    //      Update the size of the surface. Next render will create a new surface.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetSize(UINT uWidth, UINT uHeight)
    {
        if (uWidth != m_uWidth || uHeight != m_uHeight)
        {
            m_uWidth = uWidth;
            m_uHeight = uHeight;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetAlpha
    //
    //  Synopsis:
    //      Update the format of the surface. Next render will create a new surface.
    //
    //------------------------------------------------------------------------------
    void
    CRendererManager::SetAlpha(bool fUseAlpha)
    {
        if (fUseAlpha != m_fUseAlpha)
        {
            m_fUseAlpha = fUseAlpha;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetNumDesiredSamples
    //
    //  Synopsis:
    //      Update the MSAA settings of the surface. Next render will create a 
    //      new surface.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetNumDesiredSamples(UINT uNumSamples)
    {
        if (m_uNumSamples != uNumSamples)
        {
            m_uNumSamples = uNumSamples;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetAdapter
    //
    //  Synopsis:
    //      Update the current renderer. Next render will use the new renderer.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetAdapter(POINT screenSpacePoint)
    {
        CleanupInvalidDevices();
    
        //
        // After CleanupInvalidDevices, we may not have any D3D objects. Rather than
        // recreate them here, ignore the adapter update and wait for render to recreate.
        //
    
        if (m_pD3D && m_rgRenderers)
        {
            HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                if (hMon == m_pD3D->GetAdapterMonitor(i))
                {
                    m_pCurrentRenderer = m_rgRenderers[i];
                    break;
                }
            }
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::Render
    //
    //  Synopsis:
    //      Forward to the current renderer
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::Render()
    {
        return m_pCurrentRenderer ? m_pCurrentRenderer->Render() : S_OK;
    }
    
  6. Buka TriangleRenderer.h di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    #pragma once
    
    class CTriangleRenderer : public CRenderer
    {
    public:
        static HRESULT Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer);
        ~CTriangleRenderer();
    
        HRESULT Render();
    
    protected:
        HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter);
    
    private:
        CTriangleRenderer();
    
        IDirect3DVertexBuffer9 *m_pd3dVB;
    };
    
  7. Buka TriangleRenderer.cpp di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    
    //+-----------------------------------------------------------------------------
    //
    //  CTriangleRenderer
    //
    //      Subclass of CRenderer that renders a single, spinning triangle
    //
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    struct CUSTOMVERTEX
    {
        FLOAT x, y, z; 
        DWORD color;
    };
    
    #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer ctor
    //
    //------------------------------------------------------------------------------
    CTriangleRenderer::CTriangleRenderer() : CRenderer(), m_pd3dVB(NULL)
    {
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer dtor
    //
    //------------------------------------------------------------------------------
    CTriangleRenderer::~CTriangleRenderer()
    {
        SAFE_RELEASE(m_pd3dVB);
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Create
    //
    //  Synopsis:
    //      Creates the renderer
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CTriangleRenderer::Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer)
    {
        HRESULT hr = S_OK;
        
        CTriangleRenderer *pRenderer = new CTriangleRenderer();
        IFCOOM(pRenderer);
    
        IFC(pRenderer->Init(pD3D, pD3DEx, hwnd, uAdapter));
    
        *ppRenderer = pRenderer;
        pRenderer = NULL;
    
    Cleanup:
        delete pRenderer;
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Init
    //
    //  Synopsis:
    //      Override of CRenderer::Init that calls base to create the device and 
    //      then creates the CTriangleRenderer-specific resources
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CTriangleRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
    {
        HRESULT hr = S_OK;
        D3DXMATRIXA16 matView, matProj;
        D3DXVECTOR3 vEyePt(0.0f, 0.0f,-5.0f);
        D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
        D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
    
        // Call base to create the device and render target
        IFC(CRenderer::Init(pD3D, pD3DEx, hwnd, uAdapter));
    
        // Set up the VB
        CUSTOMVERTEX vertices[] =
        {
            { -1.0f, -1.0f, 0.0f, 0xffff0000, }, // x, y, z, color
            {  1.0f, -1.0f, 0.0f, 0xff00ff00, },
            {  0.0f,  1.0f, 0.0f, 0xff00ffff, },
        };
    
        IFC(m_pd3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pd3dVB, NULL));
    
        void *pVertices;
        IFC(m_pd3dVB->Lock(0, sizeof(vertices), &pVertices, 0));
        memcpy(pVertices, vertices, sizeof(vertices));
        m_pd3dVB->Unlock();
    
        // Set up the camera
        D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
        IFC(m_pd3dDevice->SetTransform(D3DTS_VIEW, &matView));
        D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f);
        IFC(m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj));
    
        // Set up the global state
        IFC(m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
        IFC(m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE));
        IFC(m_pd3dDevice->SetStreamSource(0, m_pd3dVB, 0, sizeof(CUSTOMVERTEX)));
        IFC(m_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX));
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Render
    //
    //  Synopsis:
    //      Renders the rotating triangle
    //
    //------------------------------------------------------------------------------
    HRESULT
    CTriangleRenderer::Render()
    {
        HRESULT hr = S_OK;
        D3DXMATRIXA16 matWorld;
    
        IFC(m_pd3dDevice->BeginScene());
        IFC(m_pd3dDevice->Clear(
            0,
            NULL,
            D3DCLEAR_TARGET,
            D3DCOLOR_ARGB(128, 0, 0, 128),  // NOTE: Premultiplied alpha!
            1.0f,
            0
            ));
    
        // Set up the rotation
        UINT  iTime  = GetTickCount() % 1000;
        FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
        D3DXMatrixRotationY(&matWorld, fAngle);
        IFC(m_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld));
    
        IFC(m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1));
    
        IFC(m_pd3dDevice->EndScene());
    
    Cleanup:
        return hr;
    }
    
  8. Buka stdafx.h di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    // stdafx.h : include file for standard system include files,
    // or project specific include files that are used frequently, but
    // are changed infrequently
    //
    
    #pragma once
    
    #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
    // Windows Header Files:
    #include <windows.h>
    
    #include <d3d9.h>
    #include <d3dx9.h>
    
    #include <assert.h>
    
    #include "RendererManager.h"
    #include "Renderer.h"
    #include "TriangleRenderer.h"
    
    #define IFC(x) { hr = (x); if (FAILED(hr)) goto Cleanup; }
    #define IFCOOM(x) { if ((x) == NULL) { hr = E_OUTOFMEMORY; IFC(hr); } }
    #define SAFE_RELEASE(x) { if (x) { x->Release(); x = NULL; } }
    
    
  9. Buka dllmain.cpp di Editor Kode dan ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
    static CRendererManager *pManager = NULL;
    
    static HRESULT EnsureRendererManager()
    {
        return pManager ? S_OK : CRendererManager::Create(&pManager);
    }
    
    extern "C" HRESULT WINAPI SetSize(UINT uWidth, UINT uHeight)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetSize(uWidth, uHeight);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetAlpha(BOOL fUseAlpha)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetAlpha(!!fUseAlpha);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetNumDesiredSamples(UINT uNumSamples)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetNumDesiredSamples(uNumSamples);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetAdapter(POINT screenSpacePoint)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetAdapter(screenSpacePoint);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI GetBackBufferNoRef(IDirect3DSurface9 **ppSurface)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        IFC(pManager->GetBackBufferNoRef(ppSurface));
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI Render()
    {
        assert(pManager);
    
        return pManager->Render();
    }
    
    extern "C" void WINAPI Destroy()
    {
        delete pManager;
        pManager = NULL;
    }
    
  10. Buka D3DContent.def di editor kode.

  11. Ganti kode yang dihasilkan secara otomatis dengan kode berikut.

    LIBRARY "D3DContent"
    
    EXPORTS
    
    SetSize
    SetAlpha
    SetNumDesiredSamples
    SetAdapter
    
    GetBackBufferNoRef
    Render
    Destroy
    
  12. Bangun proyek.

Langkah berikutnya

Baca juga