Skip to content

Instantly share code, notes, and snippets.

@terriblememory
Last active October 12, 2017 16:09
Show Gist options
  • Save terriblememory/cca5cede44e32e086f4c3040e8de6d23 to your computer and use it in GitHub Desktop.
Save terriblememory/cca5cede44e32e086f4c3040e8de6d23 to your computer and use it in GitHub Desktop.
Single file C++ UWP D3D11 shell
/**
App.cpp
*/
#include "pch.h"
#include <agile.h>
#include <dxgi1_4.h>
#include <d3d11_3.h>
#include <DirectXMath.h>
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::UI::Core;
using namespace Windows::UI::Input;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Microsoft::WRL;
using namespace DirectX;
static float MOUSE_SCALE = 100.0f;
static inline void ChkTrue(bool x)
{
assert(x);
if (!x) abort();
}
static inline void ChkOk(HRESULT hr)
{
ChkTrue(hr == S_OK);
}
ref class AppView sealed : public IFrameworkView
{
public:
// IFrameworkView Methods.
virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
virtual void SetWindow(Windows::UI::Core::CoreWindow^ window);
virtual void Load(Platform::String^ entryPoint);
virtual void Run();
virtual void Uninitialize();
private:
// Event handlers.
void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args);
void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args);
void OnDpiChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
void OnPointerPressed(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args);
void OnPointerReleased(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args);
void OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args);
void OnMouseMoved(Windows::Devices::Input::MouseDevice ^sender, Windows::Devices::Input::MouseEventArgs ^args);
// Internal methods.
void HandleDeviceLost();
void CreateDeviceResources();
void CreateWindowSizeDependentResources();
DXGI_MODE_ROTATION ComputeDisplayRotation();
float ConvertDipsToPixels(float dips);
void UpdatePointerButtons(Windows::UI::Core::PointerEventArgs^ args);
// Window state.
bool m_windowClosed = false;
bool m_windowVisible = true;
// Cached window and display properties.
Platform::Agile<Windows::UI::Core::CoreWindow> m_window;
float m_dpi = -1.0f;
// Direct3D objects.
Microsoft::WRL::ComPtr<ID3D11Device3> m_device;
Microsoft::WRL::ComPtr<ID3D11DeviceContext3> m_context;
Microsoft::WRL::ComPtr<IDXGISwapChain3> m_swapChain;
// Direct3D rendering objects. Required for 3D.
Microsoft::WRL::ComPtr<ID3D11RenderTargetView1> m_renderTargetView;
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> m_depthStencilView;
D3D11_VIEWPORT m_viewport = D3D11_VIEWPORT();
// Cached device properties.
D3D_FEATURE_LEVEL m_featureLevel = D3D_FEATURE_LEVEL_9_1;
Windows::Foundation::Size m_renderTargetSize = Windows::Foundation::Size();
Windows::Foundation::Size m_outputSize = Windows::Foundation::Size();
Windows::Foundation::Size m_logicalSize = Windows::Foundation::Size();
Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = Windows::Graphics::Display::DisplayOrientations::None;
Windows::Graphics::Display::DisplayOrientations m_currentOrientation = Windows::Graphics::Display::DisplayOrientations::None;
// Transforms used for display orientation.
DirectX::XMFLOAT4X4 m_orientationTransform;
// Mouse state.
float MouseDeltaX = 0.0f;
float MouseDeltaY = 0.0f;
float MouseDeltaWheel = 0.0f;
bool MouseButtonL = false;
bool MouseButtonM = false;
bool MouseButtonR = false;
// Previous pointer position. Used to compute deltas.
Windows::UI::Core::CoreCursor^ _defaultCursor;
Windows::UI::Core::CoreCursor^ _cursor;
// TODO: Add application specific state here!
};
/**
Startup view provider class.
*/
ref class FrameworkViewSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
{
public:
virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView() { return ref new AppView(); }
private:
};
/**
Application entry point.
*/
[Platform::MTAThread]
int main(Platform::Array<Platform::String^>^ args)
{
CoreApplication::Run(ref new FrameworkViewSource());
return 0;
}
/**
Initialize the view.
*/
void AppView::Initialize(CoreApplicationView^ applicationView)
{
// TODO: Add application specific initialization code here!
applicationView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &AppView::OnActivated);
}
/**
Associate a CoreWindow with the view.
*/
void AppView::SetWindow(CoreWindow^ window)
{
m_window = window;
m_logicalSize = Windows::Foundation::Size(window->Bounds.Width, window->Bounds.Height);
window->SizeChanged += ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &AppView::OnWindowSizeChanged);
window->VisibilityChanged += ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &AppView::OnVisibilityChanged);
window->Closed += ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &AppView::OnWindowClosed);
window->PointerPressed += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerPressed);
window->PointerReleased += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerReleased);
window->PointerWheelChanged += ref new Windows::Foundation::TypedEventHandler<Windows::UI::Core::CoreWindow ^, Windows::UI::Core::PointerEventArgs ^>(this, &AppView::OnPointerWheelChanged);
Windows::Devices::Input::MouseDevice::GetForCurrentView()->MouseMoved += ref new Windows::Foundation::TypedEventHandler<Windows::Devices::Input::MouseDevice ^, Windows::Devices::Input::MouseEventArgs ^>(this, &AppView::OnMouseMoved);
_defaultCursor = m_window->PointerCursor;
_cursor = _defaultCursor;
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
m_nativeOrientation = currentDisplayInformation->NativeOrientation;
m_currentOrientation = currentDisplayInformation->CurrentOrientation;
m_dpi = currentDisplayInformation->LogicalDpi;
currentDisplayInformation->DpiChanged += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnDpiChanged);
currentDisplayInformation->OrientationChanged += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnOrientationChanged);
DisplayInformation::DisplayContentsInvalidated += ref new TypedEventHandler<DisplayInformation^, Object^>(this, &AppView::OnDisplayContentsInvalidated);
// TODO: Add application specific SetWindow() code here!
}
/**
Create the D3D swap chain, etc.
*/
void AppView::Load(Platform::String^ entryPoint)
{
CreateDeviceResources();
CreateWindowSizeDependentResources();
// TODO: Add application specific Load() code here!
}
/**
Run the app.
*/
void AppView::Run()
{
while (!m_windowClosed)
{
if (m_windowVisible)
{
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
// Reset the viewport to target the whole screen.
m_context->RSSetViewports(1, &m_viewport);
// Reset render targets to the screen.
ID3D11RenderTargetView *const targets[1] = { m_renderTargetView.Get() };
m_context->OMSetRenderTargets(1, targets, m_depthStencilView.Get());
// Clear the back buffer and depth stencil view.
FLOAT clearColor[] = { 0.25f, 0.15f, 0.5f, 1 };
m_context->ClearRenderTargetView(m_renderTargetView.Get(), clearColor);
m_context->ClearDepthStencilView(m_depthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
// Get device rotation.
DXGI_MODE_ROTATION swapChainRotation = DXGI_MODE_ROTATION_IDENTITY;
m_swapChain->GetRotation(&swapChainRotation); // can fail (see docs) but ignore & just use default
// TODO: Add application specific Run() code here!
// NOTE: In particular, this is where your rendering call will happen.
// Reset mouse look state for next frame.
Windows::UI::Core::CoreWindow::GetForCurrentThread()->PointerCursor = _cursor;
MouseDeltaX = 0.0f;
MouseDeltaY = 0.0f;
MouseDeltaWheel = 0.0f;
// The first argument instructs DXGI to block until VSync, putting the application
// to sleep until the next VSync. This ensures we don't waste any cycles rendering
// frames that will never be displayed to the screen.
DXGI_PRESENT_PARAMETERS parameters = { 0 };
HRESULT hrPresent = m_swapChain->Present1(1, 0, &parameters);
// Discard the contents of the render target.
// This is a valid operation only when the existing contents will be entirely
// overwritten. If dirty or scroll rects are used, this call should be removed.
m_context->DiscardView1(m_renderTargetView.Get(), nullptr, 0);
// Discard the contents of the depth stencil.
m_context->DiscardView1(m_depthStencilView.Get(), nullptr, 0);
// If the device was removed either by a disconnection or a driver upgrade, we
// must recreate all device resources.
if (hrPresent == DXGI_ERROR_DEVICE_REMOVED ||
hrPresent == DXGI_ERROR_DEVICE_RESET)
{
HandleDeviceLost();
}
else
{
ChkOk(hrPresent);
}
}
else
{
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
}
}
}
/**
Uninitialize the view.
*/
void AppView::Uninitialize()
{
// nothing!
}
/**
Handle app view activation.
*/
void AppView::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
{
CoreWindow::GetForCurrentThread()->Activate();
}
/**
Handle window size change.
*/
void AppView::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
{
Size logicalSize(sender->Bounds.Width, sender->Bounds.Height);
if (m_logicalSize != logicalSize)
{
m_logicalSize = logicalSize;
CreateWindowSizeDependentResources();
}
}
/**
Handle window visibility change.
*/
void AppView::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
{
m_windowVisible = args->Visible;
}
/**
Handle window close.
*/
void AppView::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
{
m_windowClosed = true;
}
/**
Handle DPI change.
*/
void AppView::OnDpiChanged(DisplayInformation^ sender, Object^ args)
{
if (sender->LogicalDpi != m_dpi)
{
m_dpi = sender->LogicalDpi;
// When the display DPI changes, the logical size of the window
// (measured in Dips) also changes and needs to be updated.
m_logicalSize = Windows::Foundation::Size(m_window->Bounds.Width, m_window->Bounds.Height);
CreateWindowSizeDependentResources();
}
}
/**
Handle orientation change.
*/
void AppView::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{
if (m_currentOrientation != sender->CurrentOrientation)
{
m_currentOrientation = sender->CurrentOrientation;
CreateWindowSizeDependentResources();
}
}
/**
*/
void AppView::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args)
{
// The D3D Device is no longer valid if the default adapter changed
// since the device was created or if the device has been removed.
// First, get the information for the default adapter from when the
// device was created.
ComPtr<IDXGIDevice3> dxgiDevice;
ChkOk(m_device.As(&dxgiDevice));
ComPtr<IDXGIAdapter> deviceAdapter;
ChkOk(dxgiDevice->GetAdapter(&deviceAdapter));
ComPtr<IDXGIFactory4> deviceFactory;
ChkOk(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory)));
ComPtr<IDXGIAdapter1> previousDefaultAdapter;
ChkOk(deviceFactory->EnumAdapters1(0, &previousDefaultAdapter));
DXGI_ADAPTER_DESC1 previousDesc;
ChkOk(previousDefaultAdapter->GetDesc1(&previousDesc));
// Next, get the information for the current default adapter.
ComPtr<IDXGIFactory4> currentFactory;
ChkOk(CreateDXGIFactory1(IID_PPV_ARGS(&currentFactory)));
ComPtr<IDXGIAdapter1> currentDefaultAdapter;
ChkOk(currentFactory->EnumAdapters1(0, &currentDefaultAdapter));
DXGI_ADAPTER_DESC1 currentDesc;
ChkOk(currentDefaultAdapter->GetDesc1(&currentDesc));
// If the adapter LUIDs don't match, or if the device reports
// that it has been removed, a new D3D device must be created.
if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart ||
previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart ||
FAILED(m_device->GetDeviceRemovedReason()))
{
// Release references to resources related to the old device.
dxgiDevice = nullptr;
deviceAdapter = nullptr;
deviceFactory = nullptr;
previousDefaultAdapter = nullptr;
// Create a new device and swap chain.
HandleDeviceLost();
}
}
/**
Create the Direct3D device.
*/
void AppView::CreateDeviceResources()
{
UINT creationFlags = 0;
#if defined(_DEBUG)
HRESULT hrSdkLayersCheck = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device.
0,
D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers.
nullptr, // Any feature level will do.
0,
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
nullptr, // No need to keep the D3D device reference.
nullptr, // No need to know the feature level.
nullptr // No need to keep the D3D device context reference.
);
if (SUCCEEDED(hrSdkLayersCheck))
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
}
#endif
// This array defines the set of DirectX hardware feature levels this app will support.
// Note the ordering should be preserved.
// Don't forget to declare your application's minimum required feature level in its
// description. All applications are assumed to support 9.1 unless otherwise stated.
static const D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
HRESULT hr = D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver.
0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
creationFlags, // Set debug and Direct2D compatibility flags.
featureLevels, // List of feature levels this app can support.
ARRAYSIZE(featureLevels), // Size of the list above.
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
&device, // Returns the Direct3D device created.
&m_featureLevel, // Returns feature level of device created.
&context // Returns the device immediate context.
);
if (FAILED(hr))
{
// Initialization failed, fall back to the WARP device.
ChkOk(
D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_WARP,
0,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&device,
&m_featureLevel,
&context));
}
// Store pointers to the Direct3D 11.3 API device and immediate context.
ChkOk(device.As(&m_device));
ChkOk(context.As(&m_context));
// Create the Direct2D device object and a corresponding context.
ComPtr<IDXGIDevice3> dxgiDevice;
ChkOk(m_device.As(&dxgiDevice));
}
/**
This method determines the rotation between the display device's native
orientation and the current display orientation.
*/
DXGI_MODE_ROTATION AppView::ComputeDisplayRotation()
{
// Note: m_nativeOrientation can only be Landscape or Portrait even though
// the DisplayOrientations enum has other values.
switch (m_nativeOrientation)
{
case DisplayOrientations::Landscape:
{
switch (m_currentOrientation)
{
case DisplayOrientations::Landscape: return DXGI_MODE_ROTATION_IDENTITY;
case DisplayOrientations::Portrait: return DXGI_MODE_ROTATION_ROTATE270;
case DisplayOrientations::LandscapeFlipped: return DXGI_MODE_ROTATION_ROTATE180;
case DisplayOrientations::PortraitFlipped: return DXGI_MODE_ROTATION_ROTATE90;
}
}
break;
case DisplayOrientations::Portrait:
{
switch (m_currentOrientation)
{
case DisplayOrientations::Landscape: return DXGI_MODE_ROTATION_ROTATE90;
case DisplayOrientations::Portrait: return DXGI_MODE_ROTATION_IDENTITY;
case DisplayOrientations::LandscapeFlipped: return DXGI_MODE_ROTATION_ROTATE270;
case DisplayOrientations::PortraitFlipped: return DXGI_MODE_ROTATION_ROTATE180;
}
}
break;
}
return DXGI_MODE_ROTATION_UNSPECIFIED;
}
/**
Converts a length in device-independent pixels to a length in physical pixels.
*/
float AppView::ConvertDipsToPixels(float dips)
{
static const float dipsPerInch = 96.0f;
return floorf(dips * m_dpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
/**
These resources need to be recreated every time the window size is changed.
*/
void AppView::CreateWindowSizeDependentResources()
{
// Clear the previous window size specific context.
ID3D11RenderTargetView* nullViews[] = { nullptr };
m_context->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
m_renderTargetView = nullptr;
m_depthStencilView = nullptr;
m_context->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
// Calculate the necessary render target size in pixels.
// The std::max calls make sure we don't pass zero to D3D.
m_outputSize.Width = (std::max)(1.0f, ConvertDipsToPixels(m_logicalSize.Width));
m_outputSize.Height = (std::max)(1.0f, ConvertDipsToPixels(m_logicalSize.Height));
// The width and height of the swap chain must be based on the window's
// natively-oriented width and height. If the window is not in the native
// orientation, the dimensions must be reversed.
DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();
bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
m_renderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
m_renderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
if (m_swapChain != nullptr)
{
// The swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
lround(m_renderTargetSize.Width),
lround(m_renderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
// If the device was removed for any reason, a new device
// and swap chain will need to be created.
HandleDeviceLost();
// Everything is set up now. Do not continue execution of this
// method. HandleDeviceLost will reenter this method and correctly
// set up the new device.
return;
}
else
{
ChkOk(hr);
}
}
else
{
// Create a new swap chain using the same adapter as the existing device.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = lround(m_renderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = lround(m_renderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
swapChainDesc.Flags = 0;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
ComPtr<IDXGIDevice3> dxgiDevice;
ChkOk(m_device.As(&dxgiDevice));
ComPtr<IDXGIAdapter> dxgiAdapter;
ChkOk(dxgiDevice->GetAdapter(&dxgiAdapter));
ComPtr<IDXGIFactory4> dxgiFactory;
ChkOk(dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)));
ComPtr<IDXGISwapChain1> swapChain;
ChkOk(
dxgiFactory->CreateSwapChainForCoreWindow(
m_device.Get(),
reinterpret_cast<IUnknown*>(m_window.Get()),
&swapChainDesc,
nullptr,
&swapChain));
ChkOk(swapChain.As(&m_swapChain));
// Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
// ensures that the application will only render after each VSync, minimizing power consumption.
ChkOk(dxgiDevice->SetMaximumFrameLatency(1));
}
// Set the proper orientation for the swap chain and
// the transform for rendering to the rotated swap chain.
ChkOk(m_swapChain->SetRotation(displayRotation));
// 0-degree Z-rotation
static const XMFLOAT4X4 Rotation0(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
// 90-degree Z-rotation
static const XMFLOAT4X4 Rotation90(
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
// 180-degree Z-rotation
static const XMFLOAT4X4 Rotation180(
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
// 270-degree Z-rotation
static const XMFLOAT4X4 Rotation270(
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
switch (displayRotation)
{
case DXGI_MODE_ROTATION_IDENTITY: m_orientationTransform = Rotation0; break;
case DXGI_MODE_ROTATION_ROTATE90: m_orientationTransform = Rotation270; break;
case DXGI_MODE_ROTATION_ROTATE180: m_orientationTransform = Rotation180; break;
case DXGI_MODE_ROTATION_ROTATE270: m_orientationTransform = Rotation90; break;
default: ChkTrue(false);
}
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D1> backBuffer;
ChkOk(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)));
ChkOk(m_device->CreateRenderTargetView1(backBuffer.Get(), nullptr, &m_renderTargetView));
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC1 depthStencilDesc(
DXGI_FORMAT_D24_UNORM_S8_UINT,
lround(m_renderTargetSize.Width),
lround(m_renderTargetSize.Height),
1, // This depth stencil view has only one texture.
1, // Use a single mipmap level.
D3D11_BIND_DEPTH_STENCIL);
ComPtr<ID3D11Texture2D1> depthStencil;
ChkOk(m_device->CreateTexture2D1(&depthStencilDesc, nullptr, &depthStencil));
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
ChkOk(m_device->CreateDepthStencilView(depthStencil.Get(), &depthStencilViewDesc, &m_depthStencilView));
// Set the 3D rendering viewport to target the entire window.
m_viewport = CD3D11_VIEWPORT(0.0f, 0.0f, m_renderTargetSize.Width, m_renderTargetSize.Height);
m_context->RSSetViewports(1, &m_viewport);
// TODO: Add application specific device resource creation here!
}
/**
Recreate all device resources and set them back to the current state.
*/
void AppView::HandleDeviceLost()
{
m_swapChain = nullptr;
CreateDeviceResources();
CreateWindowSizeDependentResources();
}
/**
*/
void AppView::OnPointerPressed(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args)
{
// NOTE: If implementing ImGui wrap this in the following condition
// if (!ImGui::GetIO().WantCaptureMouse) { ... }
// to prevent our mouse look behavior when an ImGui control has
// captured the pointer.
// This assumes we're implementing some kind of "mouse look" functionality,
// i.e. if the pointer is pressed we're dragging the view direction around.
// By settings _cursor to nullptr we will hide the mouse pointer (see the
// AppView::Run method) and trigger calculation of mouse pointer deltas in
// the AppView::OnMouseMoved method.
UpdatePointerButtons(args);
_cursor = nullptr;
}
/**
*/
void AppView::OnPointerReleased(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args)
{
UpdatePointerButtons(args);
_cursor = _defaultCursor;
}
/**
*/
void AppView::OnMouseMoved(Windows::Devices::Input::MouseDevice ^sender, Windows::Devices::Input::MouseEventArgs ^args)
{
if (_cursor == nullptr)
{
float dx = float(args->MouseDelta.X) / MOUSE_SCALE;
float dy = float(args->MouseDelta.Y) / MOUSE_SCALE;
MouseDeltaX = (std::max)(-1.0f, (std::min)(+1.0f, dx));
MouseDeltaY = (std::max)(-1.0f, (std::min)(+1.0f, dy));
}
}
/**
*/
void AppView::OnPointerWheelChanged(Windows::UI::Core::CoreWindow ^sender, Windows::UI::Core::PointerEventArgs ^args)
{
auto d = args->CurrentPoint->Properties->MouseWheelDelta;
auto nd = float(d) / float(WHEEL_DELTA * MOUSE_SCALE);
MouseDeltaWheel = (std::max)(-1.0f, (std::min)(+1.0f, nd));
}
/**
*/
void AppView::UpdatePointerButtons(Windows::UI::Core::PointerEventArgs^ args)
{
auto device = args->CurrentPoint->PointerDevice;
if (device->PointerDeviceType == Windows::Devices::Input::PointerDeviceType::Mouse)
{
auto properties = args->CurrentPoint->Properties;
MouseButtonL = properties->IsLeftButtonPressed;
MouseButtonM = properties->IsMiddleButtonPressed;
MouseButtonR = properties->IsRightButtonPressed;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment