3D Win UWP App

This page goes through on how to build the 3D game for Windows 10 – it will be a Windows UWP (Universal Windows Platform) App. The UWP App means that your game will run on any Windows device, whether it’s a PC, tablet or even Xbox. It will also be available to be put on the Windows Store.

I am using Windows DirectX 12 which is an API that allows you to build a 3D game.

With Visual Studio Community, you have to download the Windows Universal tools for C++

Create a new project using Visual Studio Community. I chose the DirectX 12 App using Universal Windows. This will be our starting point for the app.

Then give your project a name. Mine is called Blindfate

Choose the Windows version – I just accepted the default.

To see what this app does, press the green play button as below. This will build the app and then run it.

You should see a rotating cube as below:

If you look at the Start menu on Windows, you will see the App there and you can run it.

Code Structure

The code has both a game loop and renderer to show the graphics to the screen. This acts as a template for our game. Read through the code and the explanations below.

Some explanation of the code files are here. This is a game template that provides a starting point for our game. We will then modify it.

My intention is to use some code from the DirectX12 Kit , DirectX Graphics Samples and sample games.

Some code is from a 3D sample game written in DirectX11, but some code is usable. There is an older source1 and newer source2 which is not quite complete 🙂 Also it is worth reading the tutorial on this sample game.

I will use some code from the DirectX Book by Frank Luna to develop the game by rewriting it – I found by reading Chapters 1 – 7 really helped me understand how DirectX12 worked, then I could pick other chapters to help when I needed it.

There is also a good starting tutorial on DirectX 12 here.

Note: the template uses C++/CX. I will eventually update the code to use C++/WinRT as this is the preferred standard now for writing Windows 10 apps. Luckily you can do this in portions as you go.

Setting Up the UWP App

If we were writing a Win32 program, it would build as a .exe file so that we can run it – this is how most programs or apps used to run. That’s how I did version 1 of the game, which is really just displaying graphics and moving around the screen using Frank Luna’s book.

Now apps for Windows run using a different structure called Universal Windows Platform. This is so that an app written for one version of Windows 10 can run on other devices. When you build the app, it runs on your Windows 10 PC but the same app also runs on the Xbox. That way the same app can also be bought on the Windows Store, regardless of device.

The main window you see when you run the app is called the CoreWindow. It is based on the Windows.ApplicationModel.CoreNamespace. More details on this is here.

The files App.h and App.cpp contain the code to build a UWP app.

The file Package.appxmanifest contains properties for our app. The files pch.h and pch.cpp are precompiled headers to help our code build faster before running our app.

A class is created called App which has been derived from IFrameworkView. A CoreWindow is created which represents the UWP app.

The App::Run() method then runs the app and also checks if Windows has any events to process. This is actually our game loop. If you read this method, it has:

  • m_main->Update() this function is used to update the game each frame at a time
  • m_main->Render() this function is used to render our game art each frame at a time
  • GetDeviceResources()->Present() this function is used to show our game art to the screen each frame at a time

A game loop also needs a timer. This is done in StepTimer.h and a class called StepTimer is created. This timer ensures smooth game animation and calculates frames per second (FPS)

The App class also handles things like what to do if the app is paused, what happens if the user resizes the window or quits the game.

Note that in App.h and App.cpp, there is some code to link in DirectX graphics to the CoreWindow – this is done via Direct3DApplicationSource.

Setting Up DirectX

Unfortunately setting up DirectX is complex. For 3D graphics, you need to create a Direct3D device and attach it to our game app. The device represents the graphics card of your computer (eg: Nvidia card).

The detailed steps to setup DirectX and also render to the screen from Microsoft is here. Also a description of the graphics pipeline for DirectX is here.

The setting up of DirectX is done in the files DeviceResources.h and DeviceResources.cpp

There is a class called DeviceResources which is created to handle initialisation of DirectX12.

The steps the game template carries out is as follows:

  • Create the graphics device ID3D12Device which represents the display adapter (graphics card). If a DirectX12 compatible card is not found, it will use the WARP device, which is a software video card
  • Create the command queue m_commandQueue – this belongs to the GPU. The CPU will feed this to the GPU command queue
  • Create descriptor heaps to store descriptors and views – m_rtvHeap is for render target views and m_dsvHeap is for depth stencil views
  • Create a command allocator m_commandAllocators[n]
  • Create the fence object m_fence – this is used to synchronise the CPU and GPU
  • Create the swap chain m_swapChain – this is the back buffer. You draw to the back buffer first before presenting it to the screen
  • Resize the back buffer to match the size of the window
  • Create a render target view m_renderTargets[n]
  • Create the depth/stencil buffer
  • Set the viewport m_screenViewport and scissor rectangles

Rendering Graphics

The template comes with a sample renderer with the files:

The steps in the Sample3DSceneRenderer is as follows:

Setting up the scene and loading the geometry or mesh:

  • Create the root signature m_rootSignature
  • Load the Vertex and Pixel shaders. Note that these are compiled from .hlsl to .cso
  • Create the pipeline state object m_pipelineState
  • Create the command list m_commandList
  • Create the geometry or mesh – this is the 3D cube consisting of a list of vertices
  • Create the vertex buffer m_vertexBuffer and upload it into the GPU – this is done using the command list (CPU -> GPU)
  • Load the geometry or mesh indices – this is the 3D cube consisting of a list of triangle indices
  • Create the index buffer m_indexBuffer and upload it into the GPU – this is done using the command list (CPU -> GPU)
  • Create the constant buffers and constant buffer views
  • Create the vertex and index buffer views
  • Wait for the GPU to finish loading
  • Create the viewport and scissor rectangle
  • Setup the camera

Updating the scene using the method Update(DX::StepTimer const& timer):

  • rotate the geometry or mesh – ie. rotate the cube
  • update the constant buffers

note – this uses the timer for smooth animation

Rendering the scene using the method Render():

  • Reset the command list
  • Set the graphics root signature
  • Set the descriptor heap
  • Bind the constant buffer to the pipeline
  • Set the viewport and scissor rectangle
  • Get the render target
  • Record the drawing commands. The geometry or mesh will be drawn as a triangle list
  • Indicate the back buffer will be used to present after the command list has executed to the render target
  • Execute the command list
  • Present the frame to the screen
  • Wait for the GPU to finish

Note: The Render() function draws one frame of the scene to the screen

Where to put Game specific code

There is a DirectXHelper.h file with miscellaneous functions to help with DirectX and also d3dx12.h that is written by Microsoft which is the D3DX12 utility for DirectX.

The game specific code is put in the files that you named the game. Mine is BlindfateMain.h and BlindfateMain.cpp You can also add extra source code files as you need if the code becomes too big.

Also you would need to update the Sample3DSceneRenderer.cpp and Sample3DSceneRenderer.h files to include more geometry as currently it has code for drawing a cube.

So far the Game class is as follows in Blindfate.h:

class BlindfateMain{public:BlindfateMain();void CreateRenderers(const std::shared_ptr& deviceResources);void Update();bool Render();void OnWindowSizeChanged(); void OnSuspending(); void OnResuming(); void OnDeviceRemoved(); private: // TODO: Replace with your own content renderers. std::unique_ptr<Sample3DSceneRenderer> m_sceneRenderer; // Rendering loop timer. DX::StepTimer m_timer; };

We will add and modify to this template that has been created as we build our game.