So, we have a pretty simple framework now for defining objects in our game world, but we really need a class for things we want to render in the world, so we will create the RC3DBaseDrawableObject class, as before, create a class and call it RC3DBaseDrawableObject and set it’s base class as IDrawable
We also want this object to be updatable and have all the goodies we have in our RC3DBaseObject, so we will also derive from that class, you RC3DBaseDrawableObject.h file should look like this now
#pragma once
#include "interfaces.h"
#include "RC3DBaseObject.h"
class RC3DBaseDrawableObject : public RC3DBaseObject, public IDrawable
{
public:
RC3DBaseDrawableObject(void);
~RC3DBaseDrawableObject(void);
};
Now, all we have to add is a pure virtual (in C# a virtual method) stub for the draw call to implement the IDrawable interface and, as we are deriving from RC3DBaseObject, which in turn implements the IUpdatable interface, we have to clear up any ambiguity (remember, interfaces in C++ are actually classes) between the calls and ensure the Update method of RC3DBaseObject is called, having done this your complete header file will look like this
#pragma once
#include "interfaces.h"
#include "RC3DBaseObject.h"
class RC3DBaseDrawableObject : public RC3DBaseObject, public IDrawable
{
public:
RC3DBaseDrawableObject(void);
~RC3DBaseDrawableObject(void);
virtual void Draw(float time, ID3D11DeviceContext* context, IRC3DCamera* camera) = 0;
using RC3DBaseObject::Update;
};
All our basic elements are in place now, but we need a mechanism to manage this framework, so we are going to now create a basic Game class, this class wont be all singing and all dancing, like most of the objects in these initial posts, but it will get us to a position where we can render objects.
Before we create this game class, we will quickly create a keyboard manager class, this will help manage user input, it works just the same as in the earlier posts, but we can control it from within our game class.
Create a class called RCKeyboardStateManager with no base class. We will put in the header all the elements we need to manage the keyboard
#pragma once
#include <dinput.h>
class RCKeyboardStateManager
{
protected:
LPDIRECTINPUTDEVICE8 keyboard;
char keyboardState[256];
public:
RCKeyboardStateManager(void);
~RCKeyboardStateManager(void);
virtual LPDIRECTINPUTDEVICE8 InitializeKeyboard(HWND hwnd);
virtual char* getKeyboardState();
virtual void readKeyboard();
LPDIRECTINPUTDEVICE8 getKeyboard();
};
Then populate the cpp file with the methods we have defined
#include "RCKeyboardStateManager.h"
RCKeyboardStateManager::RCKeyboardStateManager(void){ }
RCKeyboardStateManager::~RCKeyboardStateManager(void){ }
LPDIRECTINPUTDEVICE8 RCKeyboardStateManager::InitializeKeyboard(HWND hwnd)
{
LPDIRECTINPUT8 p_dx_KeybObject;
DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&p_dx_KeybObject, NULL);
p_dx_KeybObject->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
keyboard->SetDataFormat(&c_dfDIKeyboard);
keyboard->SetCooperativeLevel(hwnd, DISCL_BACKGROUND|DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);
keyboard->Acquire();
return keyboard;
}
char* RCKeyboardStateManager::getKeyboardState()
{
return keyboardState;
}
void RCKeyboardStateManager::readKeyboard()
{
keyboardState[0] = '\0';
HRESULT res = keyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState);
}
LPDIRECTINPUTDEVICE8 RCKeyboardStateManager::getKeyboard()
{
return keyboard;
}
We will now create a class called RC3DGame, and this class will not derive or implement anything else. In our header we will have the following includes
#include <D3D11.h>
#include <D3DX11.h>
#include <d3dcompiler.h>
#include <xnamath.h>
#include <vector>
#include "interfaces.h"
#include "RC3DGraphicsDevice.h"
#include "RC3DBaseDrawableObject.h"
#include "RC3DBaseObject.h"
#include "RCKeyboardStateManager.h"
Some we have seen before, d3dcompiler.h is used to compile the shader we will use, xnamath.h (yay! XNA!) has all the vector and matrix math goodies we need, the vector header is part of the standard template library and is anice way of managing collections of objects safely, the rest are our own headers.
We are now going to define a number of methods that we will use in our game loop (this will still live out side of the Game class for this post), we also need a graphics device, and a keyboard manager. Our RCGame.h file should now look like this
#pragma once
#include <D3D11.h>
#include <D3DX11.h>
#include <d3dcompiler.h>
#include <xnamath.h>
#include <vector>
#include "interfaces.h"
#include "RC3DGraphicsDevice.h"
#include "RC3DBaseDrawableObject.h"
#include "RC3DBaseObject.h"
#include "RCKeyboardStateManager.h"
class RC3DGame
{
public:
RC3DGame(void);
~RC3DGame(void);
// GraphicsDevice
RC3DGraphicsDevice GraphicsDevice;
// Keyboard manager
RCKeyboardStateManager KeyboardManager;
// Method to initialize the window
void Initialize(LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height, WNDPROC WinProc, int colorBrush = COLOR_BTNFACE);
void Update(float time, std::vector<IUpdateable*>* updateambles);
// Draw call
void Draw(float time, IRC3DCamera* camera, std::vector<IDrawable*>* drawables);
HRESULT CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut );
};
Game Initialize
And now to the function bodies, lets take a look at the Initialize method first
void RC3DGame::Initialize(LPCTSTR str_Title,int int_XPos, int int_YPos, int int_Width, int int_Height, WNDPROC WinProc, int colorBrush)
{
GraphicsDevice.InitializeWindow(str_Title,int_XPos, int_YPos, int_Width, int_Height, WinProc, colorBrush);
GraphicsDevice.Initialize();
KeyboardManager.InitializeKeyboard(GraphicsDevice.getWindowHandle());
}
Basically we are initializing the window, then the device, then the keyboard manager
Game Update
void RC3DGame::Update(float time, std::vector<IUpdateable*>* updateambles)
{
// Componente update
for(auto c = updateambles->begin();c != updateambles->end();c++)
{
(*c)->Update(time);
}
KeyboardManager.readKeyboard();
}
In her we use the vector to iterate through our IUpdatable objects and call there update methods, we then get the keyboard input.
Game Draw
void RC3DGame::Draw(float time,IRC3DCamera* camera, std::vector<IDrawable*>* drawables)
{
GraphicsDevice.Clear(XMFLOAT4( 0.392156862745098f, 0.5843137254901961f, 0.9294117647058824f, 1.0f),D3D11_CLEAR_DEPTH,1.0f,0);
for(auto c = drawables->begin();c != drawables->end();c++)
{
(*c)->Draw(0,GraphicsDevice.getDeviceContext(),camera);
}
GraphicsDevice.Present( 0, 0 );
}
First we clear the device, then we iterate through out vector of IDrawables and call there draw calls, and finally present to the device.
Game CompileShaderFromFile
HRESULT RC3DGame::CompileShaderFromFile( WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut )
{
HRESULT hr = S_OK;
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif
ID3DBlob* pErrorBlob;
hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel,
dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
if( FAILED(hr) )
{
if( pErrorBlob != NULL )
OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
if( pErrorBlob ) pErrorBlob->Release();
return hr;
}
if( pErrorBlob ) pErrorBlob->Release();
return S_OK;
}
This method has been totally lifted from the SDX tutorials, but not too difficult to see what’s going on here, we will see this method in action later.
We now have a game class we can use to update and render objects in the world, so why don’t we create an object class that can then be actually rendered.
I am going to take some more from the SDK tutorial to build this class, as we have not looked at loading mesh’s in yet, we will define our own geometry and use that to render a cube, we will now create a TestCube class that will derive from RC3DBaseDrawableObject to do this.
We also want to include the the RCGame.h and Interfaces.h in here as we will be using them. We are also going to create a number of member variables that we can then use to set up and render our cube. We will also add an Initialize method and implement the derived Draw call. Your TestCube.h file should look something like this
#pragma once
#include <D3D11.h>
#include <D3DX11.h>
#include <xnamath.h>
#include "rc3dbasedrawableobject.h"
#include "RC3DGame.h"
#include "Interfaces.h"
class TestCube : public RC3DBaseDrawableObject
{
protected:
ID3D11InputLayout* pVertexLayout;
ID3D11VertexShader* pVertexShader;
ID3D11PixelShader* pPixelShader;
ID3D11Buffer* pVertexBuffer;
ID3D11Buffer* pIndexBuffer;
ID3D11Buffer* pCBNeverChanges;
ID3D11Buffer* pCBChangeOnResize;
ID3D11Buffer* pCBChangesEveryFrame;
ID3D11ShaderResourceView* pTextureRV;
ID3D11SamplerState* pSamplerLinear;
public:
TestCube(void);
~TestCube(void);
RC3DGame* game;
XMFLOAT4 Color;
void Initialize(ID3D11Device* device );
void Draw(float time, ID3D11DeviceContext* context, IRC3DCamera* camera);
};
The ID3D11InputLayout describes the vertex structure and we will define and load that up in our cpp file
We then have references to the vertex and pixel shaders we are going to use to render this object.
We then have our vertex and index buffer, again will be defined later.
We then have a number of Buffers, these are new to me as they are part of the DirectX 11 API (remember I am coming from DirectX 9c!) and I will explain my understanding of them later.
Lastly we have a texture and a sampler reference for it.
We now have a definition, as ever, onto the function bodies, but wait, we still need some data definitions, the input layout, the vertex and index buffers, all these I have put at the top of the TestCube.cpp file, this makes them global to the class and so can be accessed across methods. Really I should have not been lazy and set them up right in the class, but I am itching to get something rendered :P
So, at the top of the TestCube.cpp file we have this lump of variables
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
struct SimpleVertex
{
XMFLOAT3 Pos;
XMFLOAT2 Tex;
};
struct CBChangeOnResize
{
XMMATRIX mProjection;
};
struct CBChangesEveryFrame
{
XMMATRIX mWorld;
XMFLOAT4 vMeshColor;
XMMATRIX mView;
};
SimpleVertex vertices[] =
{
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
};
WORD indices[] =
{
3,1,0,
2,1,3,
6,4,5,
7,4,6,
11,9,8,
10,9,11,
14,12,13,
15,12,14,
19,17,16,
18,17,19,
22,20,21,
23,20,22
};
layout describes the vertex structure to the shader pipeline and in XNA this would be the VertexDeclaration
SimpleVertex is the vertex structure we will use for the cube’s verts
CBChangeOnResize is a structure that is reloaded on screen resize
CBChangesEveryFrame describes what will be passed to the shader every frame
vertices and index are what will fill the vertex and index buffers to render the cube.
Our TestCube.cpp now looks like this after we have populated the member functions
#include "TestCube.h"
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
struct SimpleVertex
{
XMFLOAT3 Pos;
XMFLOAT2 Tex;
};
struct CBChangeOnResize
{
XMMATRIX mProjection;
};
struct CBChangesEveryFrame
{
XMMATRIX mWorld;
XMFLOAT4 vMeshColor;
XMMATRIX mView;
};
SimpleVertex vertices[] =
{
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, -0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, -0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, -0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, -0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, -0.5f, 0.5f ), XMFLOAT2( 0.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, -0.5f, 0.5f ), XMFLOAT2( 1.0f, 0.0f ) },
{ XMFLOAT3( 0.5f, 0.5f, 0.5f ), XMFLOAT2( 1.0f, 1.0f ) },
{ XMFLOAT3( -0.5f, 0.5f, 0.5f ), XMFLOAT2( 0.0f, 1.0f ) },
};
WORD indices[] =
{
3,1,0,
2,1,3,
6,4,5,
7,4,6,
11,9,8,
10,9,11,
14,12,13,
15,12,14,
19,17,16,
18,17,19,
22,20,21,
23,20,22
};
TestCube::TestCube(void)
{
Color = XMFLOAT4(1,1,1,1);
}
TestCube::~TestCube(void){ }
void TestCube::Initialize(ID3D11Device* device)
{
// Compile the vertex shader
ID3DBlob* pVSBlob = NULL;
HRESULT hr = game->CompileShaderFromFile( L"Tutorial07.fx", "VS", "vs_4_0", &pVSBlob );
if( FAILED( hr ) )
{
MessageBox( NULL,
L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
return;
}
// Create the vertex shader
hr = device->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &pVertexShader );
if( FAILED( hr ) )
{
pVSBlob->Release();
return;
}
UINT numElements = ARRAYSIZE( layout );
// Create the input layout
hr = device->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
pVSBlob->GetBufferSize(), &pVertexLayout );
pVSBlob->Release();
// Compile the pixel shader
ID3DBlob* pPSBlob = NULL;
hr = game->CompileShaderFromFile( L"Tutorial07.fx", "PS", "ps_4_0", &pPSBlob );
if( FAILED( hr ) )
{
MessageBox( NULL,
L"The FX file cannot be compiled. Please run this executable from the directory that contains the FX file.", L"Error", MB_OK );
return;
}
// Create the pixel shader
hr = device->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &pPixelShader );
pPSBlob->Release();
if( FAILED( hr ) )
return;
// Create vertex buffer
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( SimpleVertex ) * 24;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
InitData.pSysMem = vertices;
hr = device->CreateBuffer( &bd, &InitData, &pVertexBuffer );
if( FAILED( hr ) )
return;
// Build index
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof( WORD ) * 36;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
InitData.pSysMem = indices;
hr = device->CreateBuffer( &bd, &InitData, &pIndexBuffer );
if( FAILED( hr ) )
return;
// Create the constant buffers
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.ByteWidth = sizeof(CBChangeOnResize);
hr = device->CreateBuffer( &bd, NULL, &pCBChangeOnResize );
if( FAILED( hr ) )
return;
bd.ByteWidth = sizeof(CBChangesEveryFrame);
hr = device->CreateBuffer( &bd, NULL, &pCBChangesEveryFrame );
if( FAILED( hr ) )
return;
// Load the Texture
hr = D3DX11CreateShaderResourceViewFromFile( device, L"seafloor.dds", NULL, NULL, &pTextureRV, NULL );
if( FAILED( hr ) )
return;
// Create the sample state
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory( &sampDesc, sizeof(sampDesc) );
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
hr = device->CreateSamplerState( &sampDesc, &pSamplerLinear );
if( FAILED( hr ) )
return;
}
void TestCube::Draw(float time,ID3D11DeviceContext* context, IRC3DCamera* camera)
{
// Set the input layout
context->IASetInputLayout( pVertexLayout );
// Set vertex buffer
UINT stride = sizeof( SimpleVertex );
UINT offset = 0;
context->IASetVertexBuffers( 0, 1, &pVertexBuffer, &stride, &offset );
// Set index buffer
context->IASetIndexBuffer( pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
// Set primitive topology
context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
// Initialize the projection matrix
CBChangeOnResize cbChangesOnResize;
cbChangesOnResize.mProjection = XMMatrixTranspose( camera->getProjection() );
context->UpdateSubresource( pCBChangeOnResize, 0, NULL, &cbChangesOnResize, 0, 0 );
//
// Update variables that change once per frame
//
CBChangesEveryFrame cb;
cb.mWorld = XMMatrixTranspose( getWorld() );
cb.vMeshColor = Color;
cb.mView = XMMatrixTranspose( camera->getView() );
context->UpdateSubresource( pCBChangesEveryFrame, 0, NULL, &cb, 0, 0 );
// Render the cube
context->VSSetShader( pVertexShader, NULL, 0 );
context->VSSetConstantBuffers( 0, 1, &pCBChangeOnResize );
context->VSSetConstantBuffers( 1, 1, &pCBChangesEveryFrame );
context->PSSetShader( pPixelShader, NULL, 0 );
context->PSSetConstantBuffers( 1, 1, &pCBChangesEveryFrame );
context->PSSetShaderResources( 0, 1, &pTextureRV );
context->PSSetSamplers( 0, 1, &pSamplerLinear );
context->DrawIndexed( 36, 0, 0 );
}
As you can see we have set the ctor togive the object a default color of white. The next method we have is the HUGE Initialize, and this s the majority of the work is being done here, loading the shaders up, creating the vertex and index buffers, as well as setting the constant buffers and loading the texture and setting the sampler. Again you will see I have taken the shader and the texture from the SDK tutorials.
Our Draw call is again pretty straightforward, sets the device up with vertex and index buffers, sets the parameters in the buffers, then makes the draw call.
We now have something we can render, what we need now is a WinMain, a loop and some objects to render…
Just as before, create a main.cpp file in the solution
In here we will add all theeaders we need, create a WinProc call back as before and a WinMain function, we will also set up the game loop as before, but use our new game class to build the window and device.
#pragma once
#include <Windows.h>
#include "RC3DGame.h"
RC3DGame game;
int int_AppRunning = 1;
LRESULT CALLBACK winProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_CLOSE:
{
int_AppRunning = 0;
break;
}
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow)
{
MSG msg_Message;
int result = 0;
game.Initialize(L"RandomchaosDX11ProtoEngien",0,0,800,600,winProc);
HWND hWnd = game.GraphicsDevice.getWindowHandle();
if(hWnd != HWND(-1))
{
while(int_AppRunning)
{
if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
DispatchMessage(&msg_Message);
}
DestroyWindow(hWnd);
}
return result;
}
We can now compile and run our application, you wont see much, but it will run.
Lets add our camera, set up a list of IDrawables and I Updateables and call the game update and draw calls. Under RC3DGame game, add the following lines (don’t forget to add the header for the camera!)
RC3DCamera* camera;
std::vector<IUpdateable*> vUpdateables;
std::vector<IDrawable*> vDrawables;
We now have a camera and two lists to store references to drawable and updatable objects.
After we have initialized the graphics devic, we can initialize our camera and add it to the updateable list with in WinMain
game.Initialize(L"RandomchaosDX11ProtoEngien",0,0,800,600,winProc);
HWND hWnd = game.GraphicsDevice.getWindowHandle();
camera = new RC3DCamera(XMFLOAT3(0,0,0),XMFLOAT3(0,1,0),XMQuaternionIdentity(), game.GraphicsDevice.getViewPort().Width,game.GraphicsDevice.getViewPort().Height);
vUpdateables.push_back(camera);
Now in our main game loop we can add the draw and update calls
while(int_AppRunning)
{
if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
DispatchMessage(&msg_Message);
game.Update(0,&vUpdateables);
game.Draw(0,camera,&vDrawables);
}
Running this will just give us a nice cornflower blue screen, so lets put something in the scene to render, lest create a TestCube :)
Include the TestCube.h file. and add the following after the IDrawable list creation
Then after we have added the camera to the update list, do add this
testCube.game = &game;
testCube.setPosition(XMFLOAT3(0,0,10));
testCube.Initialize(game.GraphicsDevice.getDevice());
vUpdateables.push_back(&testCube);
vDrawables.push_back(&testCube);
We now need to add the texture and shader to the project, so right click the project, and add a new filter like this
and call it Shaders, then copy any dds texture you like into the project folder and call it seafloor.
In the Shaders filter/folder create anew file called Tutorial07.fx and paste the following into it
//--------------------------------------------------------------------------------------
// File: Tutorial07.fx
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
// Modified a little for the Randomchaos: Graphics Adventures in C++ with DirectX 11 blog
// http://randomchaosdx11adventures.blogspot.co.uk/
//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
Texture2D txDiffuse : register( t0 );
SamplerState samLinear : register( s0 );
cbuffer cbChangeOnResize : register( b0 )
{
matrix Projection;
};
cbuffer cbChangesEveryFrame : register( b1 )
{
matrix World;
float4 vMeshColor;
matrix View;
};
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output = (PS_INPUT)0;
output.Pos = mul( input.Pos, World );
output.Pos = mul( output.Pos, View );
output.Pos = mul( output.Pos, Projection );
output.Tex = input.Tex;
return output;
}
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
return txDiffuse.Sample( samLinear, input.Tex ) * vMeshColor;
}
The shader is a fair bit different to a XNA/DX9c shader isn’t it, variablesare now stored in cbuffer structu, and in the case of this shader there is no technique, just a vertex and a pixel shader.
So, we can run this now right? Nope, if you do you will get this error
We need to right click the shader file, and tell the IDE not to try and build it as we will do this at run time, change the ItemType from “HLSL Compiler” to “Does not participate in build”
If we run the project now, we will get this
AT LAST! We have rendered our cube in DirectX 11 using a basic C++ framework.
That’s all well and good, but that’s just a square of texture, how do I know it’s a cube? Well you will need to move around it, so lets wire up some keyboard controls like we have in previous posts, but the game class is already getting us the keyboard state, so we just need to react to what’s pressed.
After our WinProc callback method we can create a HandleKeyboardInput method like this
void HandleKeyboardInput()
{
float spd = .0005f;
float rotSpd = .0001f;
if (game.KeyboardManager.getKeyboardState()[DIK_ESCAPE]/128)
int_AppRunning = 0;
if (game.KeyboardManager.getKeyboardState()[DIK_LEFT]/128)
camera->Rotate(XMVectorSet(0,1,0,0),-rotSpd);
if (game.KeyboardManager.getKeyboardState()[DIK_RIGHT]/128)
camera->Rotate(XMVectorSet(0,1,0,0),rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_UP]/128)
camera->Rotate(XMVectorSet(1,0,0,0),-rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_DOWN]/128)
camera->Rotate(XMVectorSet(1,0,0,0),rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_SPACE]/128)
camera->setOrientation(XMQuaternionIdentity());
if(game.KeyboardManager.getKeyboardState()[DIK_W]/128)
camera->Translate(XMFLOAT3(0,0,spd));
if(game.KeyboardManager.getKeyboardState()[DIK_S]/128)
camera->Translate(XMFLOAT3(0,0,-spd));
if(game.KeyboardManager.getKeyboardState()[DIK_A]/128)
camera->Translate(XMFLOAT3(-spd,0,0));
if(game.KeyboardManager.getKeyboardState()[DIK_D]/128)
camera->Translate(XMFLOAT3(spd,0,0));
}
And at the top of our game loop call it
while(int_AppRunning)
{
if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
DispatchMessage(&msg_Message);
HandleKeyboardInput();
game.Update(0,&vUpdateables);
game.Draw(0,camera,&vDrawables);
}
We should now be able to run the application and move around the cube to get views like this
Or, as shown in a clip a few posts ago, this :)
What if I want to render lots of cubes? No problem, remove the references to the instance of testCube we just rendered and do this
int cubes = 8;
for(int c = 0; c < cubes;c++)
{
TestCube* testCube = new TestCube();
testCube->game = &game;
XMFLOAT3 pos;
XMFLOAT4 col;
switch(c)
{
case 0:
pos = XMFLOAT3(-1,0,1);
col = XMFLOAT4(1,1,1,1);
break;
case 1:
pos = XMFLOAT3(1,0,1);
col = XMFLOAT4(1,0,1,1);
break;
case 2:
pos = XMFLOAT3(-1,0,3);
col = XMFLOAT4(1,1,0,1);
break;
case 3:
pos = XMFLOAT3(1,0,3);
col = XMFLOAT4(0,1,1,1);
break;
case 4:
pos = XMFLOAT3(-1,2,1);
col = XMFLOAT4(0,0,1,1);
break;
case 5:
pos = XMFLOAT3(1,2,1);
col = XMFLOAT4(1,0,0,1);
break;
case 6:
pos = XMFLOAT3(-1,2,3);
col = XMFLOAT4(0,1,0,1);
break;
case 7:
pos = XMFLOAT3(1,2,3);
col = XMFLOAT4(.5,.5,.5,1);
break;
}
testCube->setPosition(pos);
testCube->Color = col;
testCube->Initialize(game.GraphicsDevice.getDevice());
vUpdateables.push_back(testCube);
vDrawables.push_back(testCube);
}
to get something like this
So, your main.cpp would look something like this
#pragma once
#include <Windows.h>
#include "RC3DGame.h"
#include "RC3DCamera.h"
#include "TestCube.h"
RC3DGame game;
RC3DCamera* camera;
std::vector<IUpdateable*> vUpdateables;
std::vector<IDrawable*> vDrawables;
int int_AppRunning = 1;
LRESULT CALLBACK winProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_CLOSE:
{
int_AppRunning = 0;
break;
}
}
return DefWindowProc(hWnd,message,wParam,lParam);
}
void HandleKeyboardInput()
{
float spd = .0005f;
float rotSpd = .0001f;
if (game.KeyboardManager.getKeyboardState()[DIK_ESCAPE]/128)
int_AppRunning = 0;
if (game.KeyboardManager.getKeyboardState()[DIK_LEFT]/128)
camera->Rotate(XMVectorSet(0,1,0,0),-rotSpd);
if (game.KeyboardManager.getKeyboardState()[DIK_RIGHT]/128)
camera->Rotate(XMVectorSet(0,1,0,0),rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_UP]/128)
camera->Rotate(XMVectorSet(1,0,0,0),-rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_DOWN]/128)
camera->Rotate(XMVectorSet(1,0,0,0),rotSpd);
if(game.KeyboardManager.getKeyboardState()[DIK_SPACE]/128)
camera->setOrientation(XMQuaternionIdentity());
if(game.KeyboardManager.getKeyboardState()[DIK_W]/128)
camera->Translate(XMFLOAT3(0,0,spd));
if(game.KeyboardManager.getKeyboardState()[DIK_S]/128)
camera->Translate(XMFLOAT3(0,0,-spd));
if(game.KeyboardManager.getKeyboardState()[DIK_A]/128)
camera->Translate(XMFLOAT3(-spd,0,0));
if(game.KeyboardManager.getKeyboardState()[DIK_D]/128)
camera->Translate(XMFLOAT3(spd,0,0));
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdshow)
{
MSG msg_Message;
int result = 0;
game.Initialize(L"RandomchaosDX11ProtoEngien",0,0,800,600,winProc);
HWND hWnd = game.GraphicsDevice.getWindowHandle();
camera = new RC3DCamera(XMFLOAT3(0,0,0),XMFLOAT3(0,1,0),XMQuaternionIdentity(), game.GraphicsDevice.getViewPort().Width,game.GraphicsDevice.getViewPort().Height);
vUpdateables.push_back(camera);
int cubes = 8;
for(int c = 0; c < cubes;c++)
{
TestCube* testCube = new TestCube();
testCube->game = &game;
XMFLOAT3 pos;
XMFLOAT4 col;
switch(c)
{
case 0:
pos = XMFLOAT3(-1,0,1);
col = XMFLOAT4(1,1,1,1);
break;
case 1:
pos = XMFLOAT3(1,0,1);
col = XMFLOAT4(1,0,1,1);
break;
case 2:
pos = XMFLOAT3(-1,0,3);
col = XMFLOAT4(1,1,0,1);
break;
case 3:
pos = XMFLOAT3(1,0,3);
col = XMFLOAT4(0,1,1,1);
break;
case 4:
pos = XMFLOAT3(-1,2,1);
col = XMFLOAT4(0,0,1,1);
break;
case 5:
pos = XMFLOAT3(1,2,1);
col = XMFLOAT4(1,0,0,1);
break;
case 6:
pos = XMFLOAT3(-1,2,3);
col = XMFLOAT4(0,1,0,1);
break;
case 7:
pos = XMFLOAT3(1,2,3);
col = XMFLOAT4(.5,.5,.5,1);
break;
}
testCube->setPosition(pos);
testCube->Color = col;
testCube->Initialize(game.GraphicsDevice.getDevice());
vUpdateables.push_back(testCube);
vDrawables.push_back(testCube);
}
if(hWnd != HWND(-1))
{
while(int_AppRunning)
{
if(PeekMessage(&msg_Message,hWnd,0,0,PM_REMOVE))
DispatchMessage(&msg_Message);
HandleKeyboardInput();
game.Update(0,&vUpdateables);
game.Draw(0,camera,&vDrawables);
}
DestroyWindow(hWnd);
}
return result;
}
Now, I guess all we have done here is create similar objects to the XNA Game, GameComponent and DrawableGameComponent. We have not even scratched the surface of a way to import assets. What I would like to do now is create a framework that would be truly useful to us XNA folk moving to C++/DirectX11, something that would give us familiar objects to work with. Not sure how far I will get, but what ever I do, ill be posting it here.
I hope you have found this of some use, please let me know what you think in the comments.
Source code is available
here.