Separate DirectX concepts into components for reuse

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Here we take the 3D graphics concepts from the previous tutorials and split them into separate code objects for reuse. We implement classes for the concepts. We define each class in a header file and implement it in a source file. We load DDS textures and apply those textures to a 3D primitive. We also incorporate elements from Direct2D, DirectWrite, WIC, and the Windows.Graphics.Display namespace.

The previous tutorials implemented the 3D graphics concepts in a Main.cpp file so you could more easily follow them. In the real world, you might want to reuse code in other apps. All DirectX 3D graphics apps need to create resources, render, and display a scene. So, you can reuse the DirectXBase and Direct3DTutorial classes with some modifications in you apps. But you might not need to retrieve and update projection matrices so you wouldn't need the BasicCamera class.

Objective: To separate 3D graphics concepts into code objects for reuse.

Prerequisites

We assume that you are familiar with C++. You also need basic experience with graphics programming concepts.

We also assume that you went through Quickstart: setting up DirectX resources and displaying an image, Creating shaders and drawing primitives, Using depth and effects on primitives, and Applying textures to primitives.

Time to complete: 20 minutes.

Instructions

1. DirectXApp

The DirectXApp class has methods that initialize the app, set the window for the app, handle events for the app window, and start the app. The DirectXApp class also has methods that handle when the app exits and when the size of the window changes. After the app starts, it runs until the app window is closed.

The DirectXApp class declares an instance of the Direct3DTutorial as the m_renderer variable. While the app runs, it continually updates, renders, and displays the scene.

#include "Direct3DTutorial.h"

ref class DirectXApp : public Windows::ApplicationModel::Core::IFrameworkView
{
internal:
    DirectXApp();

public:
    // IFrameworkView Methods
    virtual void Initialize(_In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
    virtual void SetWindow(_In_ Windows::UI::Core::CoreWindow^ window);
    virtual void Load(_In_ Platform::String^ entryPoint);
    virtual void Run();
    virtual void Uninitialize();

private:
    // Event Handlers
    void OnWindowSizeChanged(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::WindowSizeChangedEventArgs^ args
        );

    void OnLogicalDpiChanged(
        _In_ Platform::Object^ sender
        );

    void OnActivated(
        _In_ Windows::ApplicationModel::Core::CoreApplicationView^ applicationView,
        _In_ Windows::ApplicationModel::Activation::IActivatedEventArgs^ args
        );

    void OnWindowClosed(
        _In_ Windows::UI::Core::CoreWindow^ sender,
        _In_ Windows::UI::Core::CoreWindowEventArgs^ args
        );

    Direct3DTutorial^ m_renderer;
    bool m_windowClosed;
};

ref class DirectXAppSource : Windows::ApplicationModel::Core::IFrameworkViewSource
{
public:
    virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView();
};

2. DirectXBase

The DirectXBase class has methods that initialize resources, update, render, and display the scene, and set DPI. This class declares Direct2D and DirectWrite interface variables with the ComPtr smart pointer template. The DirectXBase class creates resources that depend on the device (like ID3D11Device1 and ID2D1Device) and resources that are independent of hardware (like ID2D1Factory1 and IWICImagingFactory2).

#include "DirectXSample.h"

// Helper class that initializes the DirectX APIs in the sample apps.
ref class DirectXBase abstract
{
internal:
    DirectXBase();

    virtual void Initialize(Windows::UI::Core::CoreWindow^ window, float dpi);
    virtual void CreateDeviceIndependentResources();
    virtual void CreateDeviceResources();
    virtual void SetDpi(float dpi);
    virtual void UpdateForWindowSizeChange();
    virtual void CreateWindowSizeDependentResources();
    virtual void Render() = 0;
    virtual void Present();

protected private:
    Platform::Agile<Windows::UI::Core::CoreWindow>  m_window;

    // Declare Direct2D Objects
    Microsoft::WRL::ComPtr<ID2D1Factory1>           m_d2dFactory;
    Microsoft::WRL::ComPtr<ID2D1Device>             m_d2dDevice;
    Microsoft::WRL::ComPtr<ID2D1DeviceContext>      m_d2dContext;
    Microsoft::WRL::ComPtr<ID2D1Bitmap1>            m_d2dTargetBitmap;

    // Declare DirectWrite & Windows Imaging Component Objects
    Microsoft::WRL::ComPtr<IDWriteFactory1>         m_dwriteFactory;
    Microsoft::WRL::ComPtr<IWICImagingFactory2>     m_wicFactory;

    // Direct3D Objects
    Microsoft::WRL::ComPtr<ID3D11Device1>           m_d3dDevice;
    Microsoft::WRL::ComPtr<ID3D11DeviceContext1>    m_d3dContext;
    Microsoft::WRL::ComPtr<IDXGISwapChain1>         m_swapChain;
    Microsoft::WRL::ComPtr<ID3D11RenderTargetView>  m_renderTargetView;
    Microsoft::WRL::ComPtr<ID3D11DepthStencilView>  m_depthStencilView;

    D3D_FEATURE_LEVEL                               m_featureLevel;
    Windows::Foundation::Size                       m_renderTargetSize;
    Windows::Foundation::Rect                       m_windowBounds;
    float                                           m_dpi;
    unsigned int                                    m_numBuffers;
};

3. Direct3DTutorial

The Direct3DTutorial class inherits from the DirectXBase class. So, Direct3DTutorial directly uses some of DirectXBase's methods and overrides other methods of DirectXBase. The Direct3DTutorial class declares Direct3D interface variables with the ComPtr smart pointer template.

#include "DirectXBase.h"
#include "BasicCamera.h"
#include "SampleOverlay.h"

// describes the constant buffer that will be used to draw the cube
struct ConstantBuffer
{
    float4x4 model;
    float4x4 view;
    float4x4 projection;
};

ref class Direct3DTutorial : public DirectXBase
{
internal :
    Direct3DTutorial();

    virtual void CreateDeviceIndependentResources() override;
    virtual void CreateDeviceResources() override;
    virtual void CreateWindowSizeDependentResources() override;
    virtual void Render() override;
    void Update(float timeTotal, float timeDelta);

private:
    SampleOverlay^ m_sampleOverlay;
    Microsoft::WRL::ComPtr<ID3D11InputLayout>           m_inputLayout;                // cube vertex input layout
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_vertexBuffer;               // cube vertex buffer
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_indexBuffer;                // cube index buffer
    Microsoft::WRL::ComPtr<ID3D11VertexShader>          m_vertexShader;               // cube vertex shader
    Microsoft::WRL::ComPtr<ID3D11PixelShader>           m_pixelShader;                // cube pixel shader
    Microsoft::WRL::ComPtr<ID3D11Texture2D>             m_texture;                    // cube texture
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_textureSRV;                 // cube texture view
    Microsoft::WRL::ComPtr<ID3D11SamplerState>          m_sampler;                    // cube texture sampler
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_constantBuffer;             // constant buffer resource

    unsigned int             m_indexCount;                  // cube index count
    ConstantBuffer           m_constantBufferData;          // constant buffer resource data
    BasicCamera^             m_camera;                      // scene camera
};

4. SampleOverlay

The SampleOverlay class has methods that initialize Direct2D, DirectWrite, and WIC resources. The SampleOverlay class declares interface variables with the ComPtr smart pointer template.

#include "DirectXSample.h"

ref class SampleOverlay
{
internal:
    SampleOverlay();

    void Initialize(
        _In_ ID2D1Device*         d2dDevice,
        _In_ ID2D1DeviceContext*  d2dContext,
        _In_ IWICImagingFactory*  wicFactory,
        _In_ IDWriteFactory*      dwriteFactory,
        _In_ Platform::String^    caption
        );

    void ResetDirectXResources();

    void UpdateForWindowSizeChange();

    void Render();

    float GetTitleHeightInDips();

private:

    Microsoft::WRL::ComPtr<ID2D1Factory1>           m_d2dFactory;
    Microsoft::WRL::ComPtr<ID2D1Device>             m_d2dDevice;
    Microsoft::WRL::ComPtr<ID2D1DeviceContext>      m_d2dContext;
    Microsoft::WRL::ComPtr<IDWriteFactory>          m_dwriteFactory;
    Microsoft::WRL::ComPtr<ID2D1SolidColorBrush>    m_whiteBrush;
    Microsoft::WRL::ComPtr<ID2D1DrawingStateBlock>  m_stateBlock;

    Microsoft::WRL::ComPtr<IWICImagingFactory>      m_wicFactory;
    Microsoft::WRL::ComPtr<ID2D1Bitmap>             m_logoBitmap;
    Microsoft::WRL::ComPtr<IDWriteTextLayout>       m_textLayout;

    UINT                                            m_idIncrement;
    bool                                            m_drawOverlay;
    Platform::String^                               m_sampleName;
    float                                           m_padding;
    float                                           m_textVerticalOffset;
    D2D1_SIZE_F                                     m_logoSize;
    float                                           m_overlayWidth;
};

5. BasicCamera

The BasicCamera class has methods that retrieve and update the view and projection matrices.

// a simple camera class
ref class BasicCamera
{

private:
    float3 m_position;      // the position of the camera
    float3 m_direction;     // the unit vector of the viewing direction
    float4x4 m_view;        // view matrix
    float4x4 m_projection;  // projection matrix

internal:
    void GetViewMatrix(_Out_ float4x4 *viewMatrix);
    void GetProjectionMatrix(_Out_ float4x4 *projectionMatrix);

    // this method updates the view matrix based on new position and focus coordinates
    void SetViewParameters(
        _In_ float3 eyePosition,    // the position of the camera
        _In_ float3 lookPosition,   // the point the camera should look at
        _In_ float3 up              // the durection vector for up
        );

    // this method updates the projection matrix based on new parameters
    void SetProjectionParameters(
        _In_ float minimumFieldOfView,  // the minimum horizontal or vertical field of view, in degrees
        _In_ float aspectRatio,         // the aspect ratio of the projection (width / height)
        _In_ float nearPlane,           // depth to map to 0
        _In_ float farPlane             // depth to map to 1
        );
};

6. BasicLoader

The BasicLoader class has methods that load texture, shader, and geometry data and methods that create textures, shader views, and meshes.

#include "BasicReaderWriter.h"

ref class BasicLoader
{
internal:
    BasicLoader(
        _In_ ID3D11Device* d3dDevice,
        _In_opt_ IWICImagingFactory2* wicFactory = nullptr
        );

    void LoadTexture(
        _In_ Platform::String^ filename,
        _Out_opt_ ID3D11Texture2D** texture,
        _Out_opt_ ID3D11ShaderResourceView** textureView
        );

    concurrency::task<void> LoadTextureAsync(
        _In_ Platform::String^ filename,
        _Out_opt_ ID3D11Texture2D** texture,
        _Out_opt_ ID3D11ShaderResourceView** textureView
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
        _In_ uint32 layoutDescNumElements,
        _Out_ ID3D11VertexShader** shader,
        _Out_opt_ ID3D11InputLayout** layout
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
        _In_ uint32 layoutDescNumElements,
        _Out_ ID3D11VertexShader** shader,
        _Out_opt_ ID3D11InputLayout** layout
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _Out_ ID3D11PixelShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11PixelShader** shader
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _Out_ ID3D11ComputeShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11ComputeShader** shader
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _Out_ ID3D11GeometryShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11GeometryShader** shader
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _In_reads_opt_(numEntries) const D3D11_SO_DECLARATION_ENTRY* streamOutDeclaration,
        _In_ uint32 numEntries,
        _In_reads_opt_(numStrides) const uint32* bufferStrides,
        _In_ uint32 numStrides,
        _In_ uint32 rasterizedStream,
        _Out_ ID3D11GeometryShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _In_reads_opt_(numEntries) const D3D11_SO_DECLARATION_ENTRY* streamOutDeclaration,
        _In_ uint32 numEntries,
        _In_reads_opt_(numStrides) const uint32* bufferStrides,
        _In_ uint32 numStrides,
        _In_ uint32 rasterizedStream,
        _Out_ ID3D11GeometryShader** shader
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _Out_ ID3D11HullShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11HullShader** shader
        );

    void LoadShader(
        _In_ Platform::String^ filename,
        _Out_ ID3D11DomainShader** shader
        );

    concurrency::task<void> LoadShaderAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11DomainShader** shader
        );

    void LoadMesh(
        _In_ Platform::String^ filename,
        _Out_ ID3D11Buffer** vertexBuffer,
        _Out_ ID3D11Buffer** indexBuffer,
        _Out_opt_ uint32* vertexCount,
        _Out_opt_ uint32* indexCount
        );

    concurrency::task<void> LoadMeshAsync(
        _In_ Platform::String^ filename,
        _Out_ ID3D11Buffer** vertexBuffer,
        _Out_ ID3D11Buffer** indexBuffer,
        _Out_opt_ uint32* vertexCount,
        _Out_opt_ uint32* indexCount
        );

private:
    Microsoft::WRL::ComPtr<ID3D11Device> m_d3dDevice;
    Microsoft::WRL::ComPtr<IWICImagingFactory2> m_wicFactory;
    BasicReaderWriter^ m_basicReaderWriter;

    template <class DeviceChildType>
    inline void SetDebugName(
        _In_ DeviceChildType* object,
        _In_ Platform::String^ name
        );

    Platform::String^ GetExtension(
        _In_ Platform::String^ filename
        );

    void CreateTexture(
        _In_ bool decodeAsDDS,
        _In_reads_bytes_(dataSize) byte* data,
        _In_ uint32 dataSize,
        _Out_opt_ ID3D11Texture2D** texture,
        _Out_opt_ ID3D11ShaderResourceView** textureView,
        _In_opt_ Platform::String^ debugName
        );

    void CreateInputLayout(
        _In_reads_bytes_(bytecodeSize) byte* bytecode,
        _In_ uint32 bytecodeSize,
        _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC* layoutDesc,
        _In_ uint32 layoutDescNumElements,
        _Out_ ID3D11InputLayout** layout
        );

    void CreateMesh(
        _In_ byte* meshData,
        _Out_ ID3D11Buffer** vertexBuffer,
        _Out_ ID3D11Buffer** indexBuffer,
        _Out_opt_ uint32* vertexCount,
        _Out_opt_ uint32* indexCount,
        _In_opt_ Platform::String^ debugName
        );
};

7. BasicShapes

The BasicShapes class has methods that create different geometries.

#include "BasicMath.h"

// this struct represents the vertex format for the shapes generated in the functions below
struct BasicVertex
{
    float3 pos;  // position
    float3 norm; // surface normal vector
    float2 tex;  // texture coordinate
};

// this struct represents the vertex format for all shapes generated in the functions below
struct TangentVertex
{
    float3 pos;  // position
    float2 tex;  // texture coordinate
    float3 uTan; // texture coordinate u-tangent vector
    float3 vTan; // texture coordinate v-tangent vector
};

ref class BasicShapes
{
internal:
    BasicShapes(ID3D11Device *d3dDevice);
    void CreateCube(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_opt_ unsigned int *vertexCount,
        _Out_opt_ unsigned int *indexCount
        );
    void CreateBox(
        float3 radii,
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_opt_ unsigned int *vertexCount,
        _Out_opt_ unsigned int *indexCount
        );
    void CreateSphere(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_opt_ unsigned int *vertexCount,
        _Out_opt_ unsigned int *indexCount
        );
    void CreateTangentSphere(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_opt_ unsigned int *vertexCount,
        _Out_opt_ unsigned int *indexCount
        );
    void CreateReferenceAxis(
        _Out_ ID3D11Buffer **vertexBuffer,
        _Out_ ID3D11Buffer **indexBuffer,
        _Out_opt_ unsigned int *vertexCount,
        _Out_opt_ unsigned int *indexCount
        );

private:
    Microsoft::WRL::ComPtr<ID3D11Device> m_d3dDevice;

    void CreateVertexBuffer(
        _In_ unsigned int numVertices,
        _In_ BasicVertex *vertexData,
        _Out_ ID3D11Buffer **vertexBuffer
        );

    void CreateIndexBuffer(
        _In_ unsigned int numIndices,
        _In_ unsigned short *indexData,
        _Out_ ID3D11Buffer **indexBuffer
        );

    void CreateTangentVertexBuffer(
        _In_ unsigned int numVertices,
        _In_ TangentVertex *vertexData,
        _Out_ ID3D11Buffer **vertexBuffer
        );

};

8. BasicTimer

The BasicTimer class has methods that create a timer object and that reset and update that timer object.

ref class BasicTimer
{
private:
    LARGE_INTEGER m_frequency;
    LARGE_INTEGER m_currentTime;
    LARGE_INTEGER m_startTime;
    LARGE_INTEGER m_lastTime;
    float m_total;
    float m_delta;

internal:
    BasicTimer();
    void Reset();
    void Update();
    property float Total
    {
        float get();
    }
    property float Delta
    {
        float get();
    }
};

9. DDSTextureLoader

The DDSTextureLoader source and header files declare and implement the CreateDDSTextureFromMemory function for loading a DDS texture and creating a Direct3D 11 runtime resource for that texture. You can use CreateDDSTextureFromMemory to load light-weight DDS files at run time. For a full-featured DDS file reader, writer, and texture processing pipeline, see DirectXTex and DirectXTK.

Summary

We separated 3D graphics concepts into code objects for reuse. Congratulations! You are now ready to use 3D graphics in your own apps.