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.