ФЭНДОМ


ВведениеПравить

В этой статье мы будем улучшать наш фреймворк. Как я писал ранее, фреймворк мы создаем по мере надобности, таким образом, вы можете сразу видеть его эволюцию.

В последних статьях, как вы наверное заметили, мы очень много кода копируем из урока в урок (загрузка шейдеров, инициализация буферов), поэтому постараемся избавиться от этого перенеся этот код в движок.

Кроме того мы рассмотрели две темы - шрифты и изображения, этот код также можно перенести во фреймворк.
Еще у нас был очень неудобный недостаток - мы не могли узнать размер окна в коде, из-за чего у нас были некоторые кривые моменты (например, создание проекционной матрицы).

В этот раз изменений в коде будет много, поэтому я решил выделить отдельную статью. Так же по причине того что изменений много, в этот раз я буду выкладывать сразу весь код в измененном файле. То есть если в файле не было изменений, я не выкладываю этот код, а если там изменилась хоть одна строчка, я выложу весь код этого файла, так как большинство изменений там долго описывать. В конце кода я буду писать свои комментарии.

КодПравить

В macros.h мы обновили версию, но это не особо важно.

stdafx.hПравить

#pragma once

#include <clocale>
#include <ctime>

#include <sstream>
#include <string>
#include <list>
#include <vector>
#include <map>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <d3d11.h>
#include <d3dx11.h>
#include <d3dcompiler.h>
#include <xnamath.h>

#pragma comment(lib, "d3d11.lib")
#ifdef _DEBUG
#	pragma comment(lib, "d3dx11d.lib")
#else
#	pragma comment(lib, "d3dx11.lib")
#endif

Мы добавили несколько новых заголовков.

Render.hПравить

#pragma once

namespace D3D11Framework
{
//------------------------------------------------------------------


	class Render
	{
		friend class StaticMesh;
		friend class Image;
		friend class BitmapFont;
		friend class Text;
		friend class Shader;
	public:
		Render();
		virtual ~Render();
		
		bool CreateDevice(HWND hwnd);
		void BeginFrame();
		void EndFrame();
		void Shutdown();

		virtual bool Init() = 0;
		virtual bool Draw() = 0;
		virtual void Close() = 0;

		void TurnZBufferOn();
		void TurnZBufferOff();

		void TurnOnAlphaBlending();
		void TurnOffAlphaBlending();

		void* operator new(size_t i)
		{
			return _aligned_malloc(i,16);
		}

		void operator delete(void* p)
		{
			_aligned_free(p);
		}

	protected:
		bool m_createdevice();
		bool m_createdepthstencil();
		bool m_createblendingstate();
		void m_initmatrix();		
		void m_resize();
		
		ID3D11Device *m_pd3dDevice;
		ID3D11DeviceContext *m_pImmediateContext;
		IDXGISwapChain *m_pSwapChain;
		ID3D11RenderTargetView *m_pRenderTargetView;		
		ID3D11Texture2D *m_pDepthStencil;		
		ID3D11DepthStencilView *m_pDepthStencilView;
		ID3D11DepthStencilState *m_pDepthStencilState;
		ID3D11DepthStencilState *m_pDepthDisabledStencilState;
		ID3D11BlendState *m_pAlphaEnableBlendingState;
		ID3D11BlendState *m_pAlphaDisableBlendingState;
		XMMATRIX m_Ortho;
		XMMATRIX m_Projection;

		HWND m_hwnd;
		unsigned int m_width;
		unsigned int m_height;
	};

//------------------------------------------------------------------
}

Здесь были добавлены несколько друзей класса. Из предыдущего урока были добавлены методы TurnOnAlphaBlending() и TurnOffAlphaBlending(), которые включают и отключают альфа цвет. Также добавлены 5 новых приватных метода, они будут делать то что раньше делал метод CreateDevice(). Как вы помните, там был большой код, и я разбил его на пять методов. Был удален приватный метод загружающий шейдеры. Ниже среди новых членов класса я добавил также m_width и m_height, которые содержат размер окна.

Render.cppПравить

#include "stdafx.h"
#include "Render.h"
#include "macros.h"
#include "StaticMesh.h"
#include "Image.h"
#include "BitmapFont.h"
#include "Text.h"
#include "Log.h"

using namespace D3D11Framework;

Render::Render()
{
	m_pd3dDevice = nullptr;
	m_pImmediateContext = nullptr;
	m_pSwapChain = nullptr;
	m_pRenderTargetView = nullptr;
	m_pDepthStencil = nullptr;
	m_pDepthStencilView = nullptr;
	m_pDepthStencilState = nullptr;
	m_pDepthDisabledStencilState = nullptr;
	m_pAlphaEnableBlendingState = nullptr;
	m_pAlphaDisableBlendingState = nullptr;
}

Render::~Render()
{
}

void Render::m_resize()
{
	RECT rc;
	GetClientRect( m_hwnd, &rc );
	m_width = rc.right - rc.left;
	m_height = rc.bottom - rc.top;
}

bool Render::CreateDevice(HWND hwnd)
{
	m_hwnd = hwnd;
	
	m_resize();

	if ( !m_createdevice() )
	{
		Log::Get()->Err("Не удалось создать DirectX Device");
		return false;
	}

	if ( !m_createdepthstencil() )
	{
		Log::Get()->Err("Не удалось создать буфер глубины");
		return false;
	}

	if ( !m_createblendingstate() )
	{
		Log::Get()->Err("Не удалось создать blending state");
		return false;
	}
	
	m_pImmediateContext->OMSetRenderTargets( 1, &m_pRenderTargetView, m_pDepthStencilView );
	
	D3D11_VIEWPORT vp;
	vp.Width = (FLOAT)m_width;
	vp.Height = (FLOAT)m_height;
	vp.MinDepth = 0.0f;
	vp.MaxDepth = 1.0f;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	m_pImmediateContext->RSSetViewports( 1, &vp );

	m_initmatrix();
	
	return Init();
}

bool Render::m_createdevice()
{
	UINT createDeviceFlags = 0;
#ifdef _DEBUG
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
	};
	UINT numFeatureLevels = ARRAYSIZE( featureLevels );

	DXGI_SWAP_CHAIN_DESC sd;
	ZeroMemory( &sd, sizeof( sd ) );
	sd.BufferCount = 1;
	sd.BufferDesc.Width = m_width;
	sd.BufferDesc.Height = m_height;
	sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	sd.BufferDesc.RefreshRate.Numerator = 60;
	sd.BufferDesc.RefreshRate.Denominator = 1;
	sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	sd.OutputWindow = m_hwnd;
	sd.SampleDesc.Count = 1;
	sd.SampleDesc.Quality = 0;
	sd.Windowed = TRUE;

	HRESULT hr = D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &m_pSwapChain, &m_pd3dDevice, NULL, &m_pImmediateContext );
	if( FAILED(hr) )
		return false;

	ID3D11Texture2D* pBackBuffer = nullptr;
	hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&pBackBuffer );
	if( FAILED(hr) )
		return false;
	hr = m_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
	_RELEASE(pBackBuffer);
	if( FAILED(hr) )
		return false;

	return true;
}

bool Render::m_createdepthstencil()
{	
	D3D11_TEXTURE2D_DESC descDepth;
	ZeroMemory( &descDepth, sizeof(descDepth) );
	descDepth.Width = m_width;
	descDepth.Height = m_height;
	descDepth.MipLevels = 1;
	descDepth.ArraySize = 1;
	descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	descDepth.SampleDesc.Count = 1;
	descDepth.SampleDesc.Quality = 0;
	descDepth.Usage = D3D11_USAGE_DEFAULT;
	descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	descDepth.CPUAccessFlags = 0;
	descDepth.MiscFlags = 0;
	HRESULT hr = m_pd3dDevice->CreateTexture2D( &descDepth, NULL, &m_pDepthStencil );
	if( FAILED(hr) )
		return false;

	D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
	ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
	depthStencilDesc.DepthEnable = true;
	depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS;
	depthStencilDesc.StencilEnable = true;
	depthStencilDesc.StencilReadMask = 0xFF;
	depthStencilDesc.StencilWriteMask = 0xFF;
	depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
	depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
	depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
	hr = m_pd3dDevice->CreateDepthStencilState(&depthStencilDesc, &m_pDepthStencilState);
	if(FAILED(hr)  )
		return false;

	depthStencilDesc.DepthEnable = false;
	hr = m_pd3dDevice->CreateDepthStencilState(&depthStencilDesc, &m_pDepthDisabledStencilState);
	if( FAILED(hr) )
		return false;

	D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
	ZeroMemory( &descDSV, sizeof(descDSV) );
	descDSV.Format = descDepth.Format;
	descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	descDSV.Texture2D.MipSlice = 0;
	hr = m_pd3dDevice->CreateDepthStencilView( m_pDepthStencil, &descDSV, &m_pDepthStencilView );
	if( FAILED(hr) )
		return false;

	return true;
}

bool Render::m_createblendingstate()
{
	D3D11_BLEND_DESC blendStateDescription;
	ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
	blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
	blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;
	HRESULT hr = m_pd3dDevice->CreateBlendState(&blendStateDescription, &m_pAlphaEnableBlendingState);
	if( FAILED(hr) )
		return false;

	blendStateDescription.RenderTarget[0].BlendEnable = FALSE;
	hr = m_pd3dDevice->CreateBlendState(&blendStateDescription, &m_pAlphaDisableBlendingState);
	if(  FAILED(hr))
		return false;

	return true;
}

void Render::m_initmatrix()
{
	float aspect = (float)m_width/(float)m_height;
	m_Projection = XMMatrixPerspectiveFovLH( 0.4f*3.14f, aspect, 1.0f, 1000.0f);
	m_Ortho = XMMatrixOrthographicLH((float)m_width, (float)m_height, 0.0f, 1.0f);
}

void Render::BeginFrame()
{
	float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };
	m_pImmediateContext->ClearRenderTargetView( m_pRenderTargetView, ClearColor );
	m_pImmediateContext->ClearDepthStencilView( m_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0 );
}

void Render::EndFrame()
{
	m_pSwapChain->Present( 0, 0 );
}

void Render::Shutdown()
{
	Close();

	if( m_pImmediateContext ) 
		m_pImmediateContext->ClearState();

	_RELEASE(m_pAlphaEnableBlendingState);
	_RELEASE(m_pAlphaDisableBlendingState);
	_RELEASE(m_pDepthStencil);
	_RELEASE(m_pDepthStencilView);
	_RELEASE(m_pDepthStencilState);
	_RELEASE(m_pDepthDisabledStencilState);
	_RELEASE(m_pRenderTargetView);
	_RELEASE(m_pSwapChain);
	_RELEASE(m_pImmediateContext);
	_RELEASE(m_pd3dDevice);
}

void Render::TurnZBufferOn()
{
	m_pImmediateContext->OMSetDepthStencilState(m_pDepthStencilState, 1);
}

void Render::TurnZBufferOff()
{
	m_pImmediateContext->OMSetDepthStencilState(m_pDepthDisabledStencilState, 1);
}

void Render::TurnOnAlphaBlending()
{
	float blendFactor[4];
	blendFactor[0] = 0.0f;
	blendFactor[1] = 0.0f;
	blendFactor[2] = 0.0f;
	blendFactor[3] = 0.0f;
	m_pImmediateContext->OMSetBlendState(m_pAlphaEnableBlendingState, blendFactor, 0xffffffff);
}

void Render::TurnOffAlphaBlending()
{
	float blendFactor[4];
	blendFactor[0] = 0.0f;
	blendFactor[1] = 0.0f;
	blendFactor[2] = 0.0f;
	blendFactor[3] = 0.0f;
	m_pImmediateContext->OMSetBlendState(m_pAlphaDisableBlendingState, blendFactor, 0xffffffff);
}

Как видите, здесь основное изменение, это разбиение метода CreateDevice() на несколько меньших выполняющих одну задачу.

БуферыПравить

Во всех уроках мы писали один и тот же код по инициализации буферов. Наверное вам это уже надоело. Я решил создать специальный класс со статическими методами который и будет производить всю нужную инициализацию.

Buffer.h

#pragma once

namespace D3D11Framework
{
//------------------------------------------------------------------

	class Buffer
	{
	public:
		static ID3D11Buffer* CreateVertexBuffer(ID3D11Device *device, int size, bool dynamic, const void *Mem);
		static ID3D11Buffer* CreateIndexBuffer(ID3D11Device *device, int size, bool dynamic, const void *Mem);
		static ID3D11Buffer* CreateConstantBuffer(ID3D11Device *device, int size, bool dynamic);
	};

//------------------------------------------------------------------
}

Как видите, класс содержит три статических метода. Напомню, статические методы позволят использовать эти функции без создания самого класса. Вообще, можно было даже класс не создавать вместо него создав три глобальных функции, но мне так больше нравится:)

Каждая из функций будет создавать один из трех буферов - вершинный, индексный или константный. Первый параметр - девайс, второй - размер буфера, третий определяет, будет ли буфер динамическим с возможностью изменять его данные (см урок 2D), последний содержит сами данные и есть только у первых двух.

Buffer.cpp

#include "stdafx.h"
#include "Buffer.h"

using namespace D3D11Framework;

ID3D11Buffer* Buffer::CreateVertexBuffer(ID3D11Device *device, int size, bool dynamic, const void *Mem)
{
	ID3D11Buffer *vb = nullptr;
		
	D3D11_BUFFER_DESC BufferDesc;
	BufferDesc.ByteWidth = size;
	BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;	
	BufferDesc.MiscFlags = 0;
	BufferDesc.StructureByteStride = 0;
	if (dynamic)
	{
		BufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		BufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	}
	else
	{
		BufferDesc.Usage = D3D11_USAGE_DEFAULT;
		BufferDesc.CPUAccessFlags = 0;
	}	
	
	D3D11_SUBRESOURCE_DATA Data;	
	Data.pSysMem = Mem;
	Data.SysMemPitch = 0;
	Data.SysMemSlicePitch = 0;
	HRESULT hr = device->CreateBuffer(&BufferDesc, &Data, &vb);
	if( FAILED(hr) )
		return nullptr;

	return vb;
}

ID3D11Buffer* Buffer::CreateIndexBuffer(ID3D11Device *device, int size, bool dynamic, const void *Mem)
{
	ID3D11Buffer *ib = nullptr;

	D3D11_BUFFER_DESC BufferDesc;
	BufferDesc.ByteWidth = size;
	BufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;	
	BufferDesc.MiscFlags = 0;
	BufferDesc.StructureByteStride = 0;
	if (dynamic)
	{
		BufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		BufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	}
	else
	{
		BufferDesc.Usage = D3D11_USAGE_DEFAULT;
		BufferDesc.CPUAccessFlags = 0;
	}	

	D3D11_SUBRESOURCE_DATA Data;	
	Data.pSysMem = Mem;
	Data.SysMemPitch = 0;
	Data.SysMemSlicePitch = 0;
	HRESULT hr = device->CreateBuffer(&BufferDesc, &Data, &ib);
	if( FAILED(hr) )
		return nullptr;

	return ib;
}

ID3D11Buffer* Buffer::CreateConstantBuffer(ID3D11Device *device, int size, bool dynamic)
{
	ID3D11Buffer *cb = nullptr;

	D3D11_BUFFER_DESC BufferDesc;
	BufferDesc.ByteWidth = size;
	BufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;	
	BufferDesc.MiscFlags = 0;
	BufferDesc.StructureByteStride = 0;
	if (dynamic)
	{
		BufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		BufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	}
	else
	{
		BufferDesc.Usage = D3D11_USAGE_DEFAULT;
		BufferDesc.CPUAccessFlags = 0;
	}	
	
	HRESULT hr = device->CreateBuffer(&BufferDesc, NULL, &cb);
	if( FAILED(hr) )
		return nullptr;

	return cb;
}

Здесь вам все должно быть уже понятно и без пояснений.

ШейдерыПравить

Еще один момент из-за которого мы писали один и тот же код - инициализация шейдеров. Так что и здесь напишем свой класс. Данный класс будет оберткой над шейдерами и форматом вывода, текстурами и sample state.

Shader.h

#pragma once

#include "Render.h"

namespace D3D11Framework
{
//------------------------------------------------------------------

	class Shader
	{
	public:
		Shader(Render *render);
		// данную функцию мы должны вызывать до вызова CreateShader
		void AddInputElementDesc(const char *SemanticName, DXGI_FORMAT format);

		bool CreateShader(wchar_t *namevs, wchar_t *nameps);
		bool LoadTexture(const wchar_t *name);

		void Draw();
		void Close();

	private:
		HRESULT m_compileshaderfromfile(WCHAR* FileName, LPCSTR EntryPoint, LPCSTR ShaderModel, ID3DBlob** ppBlobOut);

		Render *m_render;

		ID3D11VertexShader *m_vertexShader;
		ID3D11PixelShader *m_pixelShader;
		ID3D11InputLayout *m_layout;
		ID3D11ShaderResourceView *m_texture;
		ID3D11SamplerState *m_sampleState;

		D3D11_INPUT_ELEMENT_DESC *m_layoutformat;
		unsigned int m_numlayout;
	};

//------------------------------------------------------------------
}

Каждый объект который будет рисоваться на экран, будет теперь содержать этот класс. Первый метод позволяет нам добавить свойство вершины, в нашем коде будет возможно добавить до 8 свойств. Второй метод будет создавать вершинный и пиксельный шейдеры. Третий грузить текстуру, если она нужна.

Shader.cpp

#include "stdafx.h"
#include "Shader.h"
#include "macros.h"
#include "Log.h"

using namespace D3D11Framework;

#define MAXLAYOUT 8

Shader::Shader(Render *render)
{
	m_render = render;
	m_vertexShader = nullptr;
	m_pixelShader = nullptr;
	m_layout = nullptr;
	m_sampleState = nullptr;
	m_texture = nullptr;
	m_layoutformat = nullptr;
	m_numlayout = 0;
}

void Shader::AddInputElementDesc(const char *SemanticName, DXGI_FORMAT format)
{
	if (!m_numlayout)
	{
		m_layoutformat = new D3D11_INPUT_ELEMENT_DESC[MAXLAYOUT];
		if (!m_layoutformat)
			return;
	}
	else if (m_numlayout >= MAXLAYOUT)
		return;

	D3D11_INPUT_ELEMENT_DESC &Layout = m_layoutformat[m_numlayout];

	Layout.SemanticName = SemanticName;
	Layout.SemanticIndex = 0;
	Layout.Format = format;
		Layout.InputSlot = 0;
	if ( !m_numlayout )
		Layout.AlignedByteOffset = 0;
	else
		Layout.AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	Layout.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	Layout.InstanceDataStepRate = 0;

	m_numlayout++;
}

bool Shader::CreateShader(wchar_t *namevs, wchar_t *nameps)
{
	HRESULT hr = S_OK;
	ID3DBlob *vertexShaderBuffer = nullptr;
	hr = m_compileshaderfromfile(namevs,"VS", "vs_4_0", &vertexShaderBuffer);
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось загрузить вершинный шейдер %ls", namevs);
		return false;
	}

	ID3DBlob *pixelShaderBuffer = nullptr;
	hr = m_compileshaderfromfile(nameps,"PS", "ps_4_0", &pixelShaderBuffer);
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось загрузить пиксельный шейдер %ls", nameps);
		return false;
	}

	hr = m_render->m_pd3dDevice->CreateVertexShader(vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), NULL, &m_vertexShader);
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось создать вершинный шейдер");
		return false;
	}

	hr = m_render->m_pd3dDevice->CreatePixelShader(pixelShaderBuffer->GetBufferPointer(), pixelShaderBuffer->GetBufferSize(), NULL, &m_pixelShader);
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось создать пиксельный шейдер");
		return false;
	}
	
	hr = m_render->m_pd3dDevice->CreateInputLayout(m_layoutformat, m_numlayout, vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_layout);
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось создать формат ввода");
		return false;
	}
	_DELETE_ARRAY(m_layoutformat);

	_RELEASE(vertexShaderBuffer);
	_RELEASE(pixelShaderBuffer);

	return true;
}

HRESULT Shader::m_compileshaderfromfile(WCHAR* FileName, LPCSTR EntryPoint, LPCSTR ShaderModel, ID3DBlob** ppBlobOut )
{
	HRESULT hr = S_OK;

	DWORD ShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
	ShaderFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob *pErrorBlob = nullptr;
	hr = D3DX11CompileFromFile(FileName, NULL, NULL, EntryPoint, ShaderModel, ShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
	if( FAILED(hr) && pErrorBlob )
		OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );

	_RELEASE(pErrorBlob);
	return hr;
}

bool Shader::LoadTexture(const wchar_t *name)
{
	HRESULT hr = D3DX11CreateShaderResourceViewFromFile( m_render->m_pd3dDevice, name, NULL, NULL, &m_texture, NULL );
	if( FAILED(hr) )
	{
		Log::Get()->Err("Не удалось загрузить текстуру %ls", name);
		return false;
	}

	D3D11_SAMPLER_DESC samplerDesc;
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.BorderColor[0] = 0;
	samplerDesc.BorderColor[1] = 0;
	samplerDesc.BorderColor[2] = 0;
	samplerDesc.BorderColor[3] = 0;
	samplerDesc.MinLOD = 0;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	hr = m_render->m_pd3dDevice->CreateSamplerState(&samplerDesc, &m_sampleState);
	if(FAILED(hr))
	{
		Log::Get()->Err("Не удалось создать sample state");
		return false;
	}

	return true;
}

void Shader::Draw()
{
	m_render->m_pImmediateContext->IASetInputLayout(m_layout);
	m_render->m_pImmediateContext->VSSetShader(m_vertexShader, NULL, 0);
	m_render->m_pImmediateContext->PSSetShader(m_pixelShader, NULL, 0);
	if (m_texture)
		m_render->m_pImmediateContext->PSSetShaderResources(0, 1, &m_texture);

	if (m_sampleState)
		m_render->m_pImmediateContext->PSSetSamplers(0, 1, &m_sampleState);
}

void Shader::Close()
{
	_RELEASE(m_vertexShader);
	_RELEASE(m_pixelShader);
	_RELEASE(m_layout);
	_RELEASE(m_sampleState);
	_RELEASE(m_texture);
}

Весь код вам должен быть уже знаком. Обратите внимание на метод AddInputElementDesc(). В начале мы смотрим не является ли добавляемое свойство первым, если да, то выделяем память. Потом следим чтобы не было добавлено больше 8 свойств. А дальше просто заполняем D3D11_INPUT_ELEMENT_DESC и увеличиваем счетчик. Еще один момент, данный массив мы удаляем сразу в CreateShader() поэтому в Close() и нет его удаления - он к тому времени уже и так пуст.

Также заметьте, мы подключаем текстуру и sample state к конвееру только в случае если они были созданы, возможно в некоторых уроках нам будут ненужны текстуры.

StaticMesh.hПравить

Ну и первый класс на котором мы опробуем два нововведения.

#pragma once

#include "Render.h"

namespace D3D11Framework
{
//------------------------------------------------------------------

	class StaticMesh
	{
	public:
		StaticMesh(Render *render);

		bool Init(wchar_t *name);
		void Draw(CXMMATRIX viewmatrix);
		void Close();

		void Translate(float x, float y, float z);
		void Rotate(float angle, float x, float y, float z);
		void Scale(float x, float y, float z);
		void Identity();


		void* operator new(size_t i)
		{
			return _aligned_malloc(i,16);
		}

		void operator delete(void* p)
		{
			_aligned_free(p);
		}

	private:
		bool m_loadMS3DFile(wchar_t* name);

		void m_RenderBuffers();
		void m_SetShaderParameters(CXMMATRIX viewmatrix);
		void m_RenderShader();

		Render *m_render;

		ID3D11Buffer *m_vertexBuffer;
		ID3D11Buffer *m_indexBuffer;
		ID3D11Buffer *m_constantBuffer;
		Shader *m_shader;

		XMMATRIX m_objMatrix;
		unsigned short m_indexCount;
	};

//------------------------------------------------------------------
}

убрали несколько членов заменив их на m_shader.

StaticMesh.cppПравить

#include "stdafx.h"
#include "StaticMesh.h"
#include "ms3dspec.h"
#include <fstream>
#include "macros.h"
#include "Util.h"
#include "Shader.h"
#include "Buffer.h"

using namespace D3D11Framework;
using namespace std;

struct Vertex
{
	XMFLOAT3 Pos;
	XMFLOAT2 Tex;
};

struct ConstantBuffer
{
	XMMATRIX WVP;
};

StaticMesh::StaticMesh(Render *render)
{
	m_render = render;
	m_vertexBuffer = nullptr;
	m_indexBuffer = nullptr;
	m_constantBuffer = nullptr;
	m_shader = nullptr;
}

bool StaticMesh::Init(wchar_t *name)
{
	Identity();

	m_shader = new Shader(m_render);
	if ( !m_shader )
		return false;
	
	m_shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	m_shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	if ( !m_shader->CreateShader(L"mesh.vs", L"mesh.ps") )
		return false;

	if( !m_loadMS3DFile(name) )
		return false;

	return true;
}

bool StaticMesh::m_loadMS3DFile(wchar_t *Filename)
{
	unsigned short VertexCount = 0;
	unsigned short TriangleCount = 0;
	unsigned short GroupCount = 0;
	unsigned short MaterialCount = 0;
	MS3DVertex *pMS3DVertices = nullptr;
	MS3DTriangle *pMS3DTriangles = nullptr;
	MS3DGroup *pMS3DGroups = nullptr;
	MS3DMaterial *pMS3DMaterials = nullptr;
	MS3DHeader header;
	
	ifstream fin;

	fin.open( Filename,std::ios::binary );
	fin.read((char*)(&(header)), sizeof(header));
	if (header.version!=3 && header.version!=4)
		return false;

	fin.read((char*)(&VertexCount), sizeof(unsigned short));
	pMS3DVertices = new MS3DVertex[VertexCount];
	fin.read((char*)pMS3DVertices, VertexCount * sizeof(MS3DVertex));

	fin.read((char*)(&TriangleCount), sizeof(unsigned short));
	pMS3DTriangles = new MS3DTriangle[TriangleCount];
	fin.read((char*)pMS3DTriangles, TriangleCount * sizeof(MS3DTriangle));

	fin.read((char*)(&GroupCount), sizeof(unsigned short));
	pMS3DGroups = new MS3DGroup[GroupCount];
	for (int i = 0; i < GroupCount; i++)
	{
		fin.read((char*)&(pMS3DGroups[i].flags), sizeof(unsigned char));
		fin.read((char*)&(pMS3DGroups[i].name), sizeof(char[32]));
		fin.read((char*)&(pMS3DGroups[i].numtriangles), sizeof(unsigned short));
		unsigned short triCount = pMS3DGroups[i].numtriangles;
		pMS3DGroups[i].triangleIndices = new unsigned short[triCount];
		fin.read((char*)(pMS3DGroups[i].triangleIndices), sizeof(unsigned short) * triCount);
		fin.read((char*)&(pMS3DGroups[i].materialIndex), sizeof(char));		
	}

	fin.read((char*)(&MaterialCount),sizeof(unsigned short));
	pMS3DMaterials = new MS3DMaterial[MaterialCount];
	fin.read((char*)pMS3DMaterials, MaterialCount * sizeof(MS3DMaterial));
	
	fin.close();

	m_indexCount = TriangleCount*3;
	unsigned short *indices = new unsigned short[m_indexCount];
	if(!indices)
		return false;
	Vertex *vertices = new Vertex[VertexCount];
	if(!vertices)
		return false;

	for (int i = 0; i < TriangleCount; i++ )
	{
		indices[3*i+0] = pMS3DTriangles[i].vertexIndices[0];
		indices[3*i+1] = pMS3DTriangles[i].vertexIndices[1];
		indices[3*i+2] = pMS3DTriangles[i].vertexIndices[2];
	}

	for (int i = 0; i < VertexCount; i++)
	{
		vertices[i].Pos.x = pMS3DVertices[i].vertex[0];
		vertices[i].Pos.y = pMS3DVertices[i].vertex[1];
		vertices[i].Pos.z = pMS3DVertices[i].vertex[2];

		for (int j = 0; j < TriangleCount; j++ )
		{
			if (i == pMS3DTriangles[j].vertexIndices[0])
			{
				vertices[i].Tex.x = pMS3DTriangles[j].s[0];
				vertices[i].Tex.y = pMS3DTriangles[j].t[0];
			}
			else if(i == pMS3DTriangles[j].vertexIndices[1])
			{
				vertices[i].Tex.x = pMS3DTriangles[j].s[1];
				vertices[i].Tex.y = pMS3DTriangles[j].t[1];
			}
			else if(i == pMS3DTriangles[j].vertexIndices[2])
			{
				vertices[i].Tex.x = pMS3DTriangles[j].s[2];
				vertices[i].Tex.y = pMS3DTriangles[j].t[2];
			}
			else
				continue;
			break;
		}
	}

	wchar_t *name = CharToWChar(pMS3DMaterials[0].texture);
	if ( !m_shader->LoadTexture(name) )
		return false;
	_DELETE_ARRAY(name);

	_DELETE_ARRAY(pMS3DMaterials);
	if (pMS3DGroups != nullptr)
	{
		for (int i = 0; i < GroupCount; i++)
			_DELETE_ARRAY(pMS3DGroups[i].triangleIndices);
		_DELETE_ARRAY(pMS3DGroups);
	}
	_DELETE_ARRAY(pMS3DTriangles);
	_DELETE_ARRAY(pMS3DVertices);

	m_vertexBuffer = Buffer::CreateVertexBuffer(m_render->m_pd3dDevice, sizeof(Vertex)*VertexCount, false, vertices);
	if (!m_vertexBuffer)
		return false;

	m_indexBuffer = Buffer::CreateIndexBuffer(m_render->m_pd3dDevice, sizeof(unsigned short)*m_indexCount, false, indices);
	if (!m_indexBuffer)
		return false;
	
	m_constantBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(ConstantBuffer), false);
	if (!m_constantBuffer)
		return false;

	_DELETE_ARRAY(vertices);
	_DELETE_ARRAY(indices);

	return true;
}

void StaticMesh::Draw(CXMMATRIX viewmatrix)
{
	m_RenderBuffers();
	m_SetShaderParameters(viewmatrix);
	m_RenderShader();
}

void StaticMesh::m_RenderBuffers()
{
	unsigned int stride = sizeof(Vertex); 
	unsigned int offset = 0;
	m_render->m_pImmediateContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
	m_render->m_pImmediateContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R16_UINT, 0);
	m_render->m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

void StaticMesh::m_SetShaderParameters(CXMMATRIX viewmatrix)
{
	XMMATRIX WVP = m_objMatrix * viewmatrix * m_render->m_Projection;	
	ConstantBuffer cb;
	cb.WVP = XMMatrixTranspose(WVP);
	m_render->m_pImmediateContext->UpdateSubresource( m_constantBuffer, 0, NULL, &cb, 0, 0 );

	m_render->m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_constantBuffer);
}

void StaticMesh::m_RenderShader()
{
	m_shader->Draw();
	m_render->m_pImmediateContext->DrawIndexed(m_indexCount, 0, 0);
}

void StaticMesh::Close()
{
	_RELEASE(m_indexBuffer);
	_RELEASE(m_vertexBuffer);
	_RELEASE(m_constantBuffer);
	_CLOSE(m_shader);
}

void StaticMesh::Translate(float x, float y, float z)
{
	m_objMatrix *= XMMatrixTranslation(x,y,z);
}

void StaticMesh::Rotate(float angle, float x, float y, float z)
{
	XMVECTOR v = XMVectorSet(x,y,z,0.0f);
	m_objMatrix *= XMMatrixRotationAxis(v,angle);
}

void StaticMesh::Scale(float x, float y, float z)
{
	m_objMatrix *= XMMatrixScaling(x,y,z);
}

void StaticMesh::Identity()
{
	m_objMatrix = XMMatrixIdentity();
}

Мы уменьшили код сразу на сто строчек:)

bool StaticMesh::Init(wchar_t *name)
{
	Identity();

	m_shader = new Shader(m_render);
	if ( !m_shader )
		return false;
	
	m_shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	m_shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	if ( !m_shader->CreateShader(L"mesh.vs", L"mesh.ps") )
		return false;

	if( !m_loadMS3DFile(name) )
		return false;

	return true;
}

Здесь мы инициализируем шейдер. В начале мы добавляем свойства вершины методом AddInputElementDesc(), передавая ему имя и тип. затем создаем шейдеры методом CreateShader(). Текстура будет как и раньше, грузится в m_loadMS3DFile().

Image.hПравить

Этот класс будет отвечать за вывод изображений на экран

#pragma once

#include "Render.h"

namespace D3D11Framework
{
//------------------------------------------------------------------
	
	class Image
	{
	public:
		Image(Render *render);

		bool Init(const wchar_t *name, float bitmapWidth, float bitmapHeight);
		void Draw(float positionX, float positionY);
		void Close();

	private:
		bool m_InitBuffers();
		void m_RenderBuffers();
		void m_SetShaderParameters(float x, float y);
		void m_RenderShader();

		Render *m_render;
		
		ID3D11Buffer *m_vertexBuffer; 
		ID3D11Buffer *m_indexBuffer;
		ID3D11Buffer *m_constantBuffer;
		Shader *m_shader;

		float m_bitmapWidth, m_bitmapHeight;
		float m_previousPosX, m_previousPosY;
	};

//------------------------------------------------------------------
}

Image.cppПравить

#include "stdafx.h"
#include "Image.h"
#include "Shader.h"
#include "macros.h"
#include "Log.h"
#include "Buffer.h"

using namespace D3D11Framework;

struct Vertex
{
	XMFLOAT3 pos;
	XMFLOAT2 tex;
};

struct ConstantBuffer
{
	XMMATRIX Ortho;
};

Image::Image(Render *render)
{
	m_render = render;
	m_vertexBuffer = nullptr;
	m_indexBuffer = nullptr;
	m_constantBuffer = nullptr;
	m_shader = nullptr;
}

bool Image::Init(const wchar_t *textureFilename, float bitmapWidth, float bitmapHeight)
{
	m_bitmapWidth = bitmapWidth;
	m_bitmapHeight = bitmapHeight;
	m_previousPosX = -1;
	m_previousPosY = -1;

	if( !m_InitBuffers() )
		return false;

	m_shader = new Shader(m_render);
	if (!m_shader)
		return false;
	if ( !m_shader->LoadTexture(textureFilename) )
		return false;
	m_shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	m_shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	if ( !m_shader->CreateShader(L"image.vs", L"image.ps") )
		return false;

	return true;
}

bool Image::m_InitBuffers()
{
	Vertex vertices[4];

	signed int centreW = m_render->m_width/2 * -1;
	signed int centreH = m_render->m_height/2;
	float left = (float)centreW;
	float right = left + m_bitmapWidth;
	float top = (float)centreH;
	float bottom = top - m_bitmapHeight;
	
	vertices[0].pos = XMFLOAT3(left, top, 0.0f);
	vertices[0].tex = XMFLOAT2(0.0f, 0.0f);

	vertices[1].pos = XMFLOAT3(right, bottom, 0.0f);
	vertices[1].tex = XMFLOAT2(1.0f, 1.0f);

	vertices[2].pos = XMFLOAT3(left, bottom, 0.0f);
	vertices[2].tex = XMFLOAT2(0.0f, 1.0f);

	vertices[3].pos = XMFLOAT3(right, top, 0.0f);
	vertices[3].tex = XMFLOAT2(1.0f, 0.0f);
		
	unsigned long indices[6] = 
	{
		0,1,2,
		0,3,1
	};

	m_vertexBuffer = Buffer::CreateVertexBuffer(m_render->m_pd3dDevice, sizeof(Vertex)*4, false, &vertices);
	if (!m_vertexBuffer)
		return false;

	m_indexBuffer = Buffer::CreateIndexBuffer(m_render->m_pd3dDevice, sizeof(unsigned long)*6, false, &indices);
	if (!m_indexBuffer)
		return false;

	m_constantBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(ConstantBuffer), false);
	if (!m_constantBuffer)
		return false;
	
	return true;
}

void Image::Draw(float positionX, float positionY)
{
	m_RenderBuffers();
	m_SetShaderParameters(positionX, positionY);
	m_RenderShader();
}

void Image::m_RenderBuffers()
{
	unsigned int stride = sizeof(Vertex); 
	unsigned int offset = 0;
	m_render->m_pImmediateContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
	m_render->m_pImmediateContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
	m_render->m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

void Image::m_SetShaderParameters(float x, float y)
{	
	XMMATRIX objmatrix = XMMatrixTranslation(x, -y, 0.0f);
	XMMATRIX wvp = objmatrix*m_render->m_Ortho;
	ConstantBuffer cb;
	cb.Ortho = XMMatrixTranspose(wvp);
	m_render->m_pImmediateContext->UpdateSubresource( m_constantBuffer, 0, NULL, &cb, 0, 0 );

	m_render->m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_constantBuffer);
}

void Image::m_RenderShader()
{
	m_shader->Draw();
	m_render->m_pImmediateContext->DrawIndexed(6, 0, 0);
}

void Image::Close()
{
	_RELEASE(m_vertexBuffer);
	_RELEASE(m_indexBuffer);
	_RELEASE(m_constantBuffer);
	_CLOSE(m_shader);
}

Обратите внимание, класс Image отличается от одноименного класса из урока по двухмерной графике, я убрал динамический вершинный буфер, и уменьшил число затрачиваемых вершин (было 6, здесь же я использую только 4 вершины)

BitmapFont.hПравить

#pragma once

#include "Render.h"

namespace D3D11Framework
{
//------------------------------------------------------------------
	
	struct VertexFont
	{
		XMFLOAT3 pos;
		XMFLOAT2 tex;
	};

	class BitmapFont
	{
	private:
		struct CharDesc
		{
			CharDesc() : srcX(0), srcY(0), srcW(0), srcH(0), xOff(0), yOff(0), xAdv(0) {}

			short srcX;
			short srcY;
			short srcW;
			short srcH;
			short xOff;
			short yOff;
			short xAdv;
		};	

		struct ConstantBuffer
		{
			XMMATRIX WVP;
		};
		struct PixelBufferType
		{
			XMFLOAT4 pixelColor;
		};
	public:
		BitmapFont(Render *render);

		bool Init(char *fontFilename);
		void Draw(unsigned int index, float r, float g, float b, float x, float y);
		void BuildVertexArray(VertexFont *vert, int numvert, const wchar_t *sentence);
		void Close();
		
	private:
		bool m_parse(char *fontFilename);
		void m_SetShaderParameters(float r, float g, float b, float x, float y);

		Render *m_render;
		ID3D11Buffer *m_constantBuffer;
		ID3D11Buffer *m_pixelBuffer;
		Shader *m_shader;
	
		unsigned short m_WidthTex;
		unsigned short m_HeightTex;
		std::wstring m_file;
		std::map <int, CharDesc> m_Chars;
	};

//------------------------------------------------------------------
}

BitmapFont.cppПравить

#include "stdafx.h"
#include "BitmapFont.h"
#include <fstream>
#include "macros.h"
#include "Util.h"
#include "Shader.h"
#include "Buffer.h"

using namespace D3D11Framework;
using namespace std;

BitmapFont::BitmapFont(Render *render)
{
	m_render = render;
	m_constantBuffer = nullptr;
	m_pixelBuffer = nullptr;
	m_shader = nullptr;
	m_WidthTex = 0;
	m_HeightTex = 0;
}

bool BitmapFont::Init(char *fontFilename)
{
	if ( !m_parse(fontFilename) )
		return false;

	m_shader = new Shader(m_render);
	if ( !m_shader )
		return false;

	if ( !m_shader->LoadTexture(m_file.c_str()) )
		return false;
	m_shader->AddInputElementDesc("POSITION", DXGI_FORMAT_R32G32B32_FLOAT);
	m_shader->AddInputElementDesc("TEXCOORD", DXGI_FORMAT_R32G32_FLOAT);
	if ( !m_shader->CreateShader(L"BitmapFont.vs", L"BitmapFont.ps") )
		return false;


	m_constantBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(ConstantBuffer), false);
	if (!m_constantBuffer)
		return false;

	m_pixelBuffer = Buffer::CreateConstantBuffer(m_render->m_pd3dDevice, sizeof(PixelBufferType), false);
	if (!m_pixelBuffer)
		return false;

	return true;
}

bool BitmapFont::m_parse(char *fontFilename)
{
	ifstream fin;
	fin.open(fontFilename);
	if(fin.fail())
		return false;

	string Line;
	string Read, Key, Value;
	size_t i;
	while( !fin.eof() )
	{
		std::stringstream LineStream;
		std::getline( fin, Line );
		LineStream << Line;
		
		LineStream >> Read;
		if( Read == "common" )
		{
			while( !LineStream.eof() )
			{
				std::stringstream Converter;
				LineStream >> Read;
				i = Read.find( '=' );
				Key = Read.substr( 0, i );
				Value = Read.substr( i + 1 );

				Converter << Value;
				if( Key == "scaleW" )
					Converter >> m_WidthTex;
				else if( Key == "scaleH" )
					Converter >> m_HeightTex;
			}
		}
		else if( Read == "page" )
		{
			while( !LineStream.eof() )
			{
				std::stringstream Converter;
				LineStream >> Read;
				i = Read.find( '=' );
				Key = Read.substr( 0, i );
				Value = Read.substr( i + 1 );

				std::string str;
				Converter << Value;
				if( Key == "file" )
				{
					Converter >> str;
					wchar_t *name = CharToWChar((char*)str.substr(1, Value.length()-2).c_str());
					m_file = name;
					_DELETE_ARRAY(name);
				}
			}
		}
		else if( Read == "char" )
		{
			unsigned short CharID = 0;
			CharDesc chard;

			while( !LineStream.eof() )
			{
				std::stringstream Converter;
				LineStream >> Read;
				i = Read.find( '=' );
				Key = Read.substr( 0, i );
				Value = Read.substr( i + 1 );

				Converter << Value;
				if( Key == "id" )
					Converter >> CharID;
				else if( Key == "x" )
					Converter >> chard.srcX;
				else if( Key == "y" )
					Converter >> chard.srcY;
				else if( Key == "width" )
					Converter >> chard.srcW;
				else if( Key == "height" )
					Converter >> chard.srcH;
				else if( Key == "xoffset" )
					Converter >> chard.xOff;
				else if( Key == "yoffset" )
					Converter >> chard.yOff;
				else if( Key == "xadvance" )
					Converter >> chard.xAdv;
			}
			m_Chars.insert(std::pair<int,CharDesc>(CharID,chard));
		}
	}

	fin.close();

	return true;
}

void BitmapFont::BuildVertexArray(VertexFont *vertex, int numvert, const wchar_t *sentence)
{
	int numLetters = (int)wcslen(sentence);
	// следим чтобы число букв не было больше числа вершин
	if ( numLetters*4 > numvert )
		numLetters = numvert/4;
			
	float drawX = (float) m_render->m_width/2 * -1;
	float drawY = (float) m_render->m_height/2;

	int index = 0;		
	for(int i=0; i<numLetters; i++)
	{
		float CharX = m_Chars[sentence[i]].srcX;
		float CharY = m_Chars[sentence[i]].srcY;
		float Width = m_Chars[sentence[i]].srcW;
		float Height = m_Chars[sentence[i]].srcH;
		float OffsetX = m_Chars[sentence[i]].xOff;
		float OffsetY = m_Chars[sentence[i]].yOff;

		float left = drawX + OffsetX;
		float right = left + Width;
		float top = drawY - OffsetY;
		float bottom = top - Height;
		float lefttex = CharX/m_WidthTex;
		float righttex = (CharX+Width)/m_WidthTex;
		float toptex = CharY/m_HeightTex;
		float bottomtex = (CharY+Height)/m_HeightTex;
					
		vertex[index].pos = XMFLOAT3(left, top, 0.0f);
		vertex[index].tex = XMFLOAT2(lefttex, toptex);
		index++;		
		vertex[index].pos = XMFLOAT3(right, bottom, 0.0f);
		vertex[index].tex = XMFLOAT2(righttex, bottomtex);
		index++;
		vertex[index].pos = XMFLOAT3(left, bottom, 0.0f);
		vertex[index].tex = XMFLOAT2(lefttex, bottomtex);
		index++;
		vertex[index].pos = XMFLOAT3(right, top, 0.0f);
		vertex[index].tex = XMFLOAT2(righttex, toptex);
		index++;
		
		drawX += m_Chars[sentence[i]].xAdv;
	}
}

void BitmapFont::Draw(unsigned int index,float r, float g, float b, float x, float y)
{
	m_SetShaderParameters(r, g, b, x, y);

	m_shader->Draw();
	m_render->m_pImmediateContext->DrawIndexed(index, 0, 0);
}

void BitmapFont::m_SetShaderParameters(float r, float g, float b, float x, float y)
{
	XMMATRIX objmatrix = XMMatrixTranslation(x,-y,0);
	XMMATRIX wvp = objmatrix*m_render->m_Ortho;
	ConstantBuffer cb;
	cb.WVP = XMMatrixTranspose(wvp);
	m_render->m_pImmediateContext->UpdateSubresource( m_constantBuffer, 0, NULL, &cb, 0, 0 );

	m_render->m_pImmediateContext->VSSetConstantBuffers(0, 1, &m_constantBuffer);
	
	PixelBufferType pb;
	pb.pixelColor = XMFLOAT4(r, g, b, 1.0f);
	m_render->m_pImmediateContext->UpdateSubresource( m_pixelBuffer, 0, NULL, &pb, 0, 0 );	

	m_render->m_pImmediateContext->PSSetConstantBuffers(0, 1, &m_pixelBuffer);
}

void BitmapFont::Close()
{
	_RELEASE(m_constantBuffer);
	_RELEASE(m_pixelBuffer);
	_CLOSE(m_shader);

}

Здесь, как и в классе Image, я уменьшил число вершин одной буквы с 6 до 4.

Text.hПравить

#pragma once

#include "BitmapFont.h"

namespace D3D11Framework
{
//------------------------------------------------------------------

	class Text
	{
	public:
		Text(Render *render, BitmapFont *font);

		// text - текст который нужно вывести
		// static - если true, то данный текст нельзя изменить
		// size - максимальное количество букв которые можно вывести. 0 означает что это число равно размеру text
		bool Init(const std::wstring &text, bool statictext=true, int charsize=0);
		void Draw(float r, float g, float b, float x, float y);
		void Close();

		bool SetText(const std::wstring &text);

	private:
		bool m_InitBuffers(const std::wstring &text);
		void m_RenderBuffers();
		bool m_updatebuffer(const std::wstring &text);

		Render *m_render;

		BitmapFont *m_font;
		ID3D11Buffer *m_vertexBuffer; 
		ID3D11Buffer *m_indexBuffer;	
		int m_numindex;
		int m_numdrawindex;
		int m_numvertex;

		bool m_static;
		int m_size;
	};

//------------------------------------------------------------------
}

По поводу текста хочу сделать одно замечание. Как вы помните, в уроке по выводу текста, наш Text имел один большой недостаток, его текст нельзя было изменить. Так вот, здесь мы решили эту проблему – текст можно сделать статическим (в таком случае строку нельзя будет изменить) и динамическим (текст можно менять в любой момент).

По умолчанию все объекты класса Text – статические. Почему сразу не сделать их динамическими? Для работы в динамическом тексте мы используем динамический буфер вершин и чтение из памяти видеокарты, а это может сильно ударить по производительности, а ведь большинство текстов за всю работу не будут меняться, так зачем на пустом месте снижать производительность?

Чтобы сделать текст динамическим, нужно в инициализации вторым параметром поставить false:

text1->Init(L"Hello",false);

Теперь, когда вам надо будет изменить текст, просто вызовите SetText() с новым текстом. В статическом тексте, эта команда не дает никакого эффекта, а вот в динамическом меняет текст.

Текст можно сделать статическим/динамическим только при его инициализации, и позже уже нельзя будет изменить.

С динамическим текстом возникает еще одна проблема – что если новый текст будет больше старого? Данная проблема также решена во фреймворке. По умолчанию новый текст может быть меньше или равен старому (первоначальному, введенному в Init()). Если новый текст больше старого, то лишние символы будут отброшены. Но вы можете сразу во время инициализации задать максимальное количество букв в тексте:

text1->Init(L"Hello",false,10);

Не смотря на то, что введенное слово состоит из 5 символов, в будущем вы сможете выводить другой текст размером меньше или равным 10 символов, о чем указывает последний параметр.

Text.cppПравить

#include "stdafx.h"
#include "Text.h"
#include "macros.h"
#include "Buffer.h"

using namespace D3D11Framework;

Text::Text(Render *render, BitmapFont *font)
{
	m_render = render;
	m_font = font;
	m_vertexBuffer = nullptr;
	m_indexBuffer = nullptr;
	m_numindex = 0;
	m_numdrawindex = 0;
	m_numvertex = 0;
	m_static = true;
	m_size = 0;
}

bool Text::Init(const std::wstring &text, bool statictext, int charsize)
{
	m_static = statictext;
	m_size = charsize;
	if ( !m_InitBuffers(text) )
		return false;

	return true;
}

bool Text::m_InitBuffers(const std::wstring &text)
{
	if (!m_size || (m_size<text.size()) )
		m_size = text.size();

	m_numvertex = m_size*4;
	m_numindex = m_size*6;
	m_numdrawindex = text.size()*6;
	if (m_numdrawindex > m_numindex)
		m_numdrawindex = m_numindex;
		
	VertexFont *vertex = new VertexFont[m_numvertex];
	if(!vertex)
		return false;

	unsigned long *indices = new unsigned long[m_numindex];
	if(!indices)
		return false;

	m_font->BuildVertexArray(vertex, m_numvertex, text.c_str());
	
	for(int i=0; i<m_numindex/6; i++)
	{
		indices[i*6+0] = i*4+0;
		indices[i*6+1] = i*4+1;
		indices[i*6+2] = i*4+2;
		indices[i*6+3] = i*4+0;
		indices[i*6+4] = i*4+3;
		indices[i*6+5] = i*4+1;
	}

	m_vertexBuffer = Buffer::CreateVertexBuffer(m_render->m_pd3dDevice, sizeof(VertexFont)*m_numvertex, !m_static, vertex);
	if (!m_vertexBuffer)
		return false;

	m_indexBuffer = Buffer::CreateIndexBuffer(m_render->m_pd3dDevice, sizeof(unsigned long)*m_numindex, false, indices);
	if (!m_indexBuffer)
		return false;
	
	_DELETE_ARRAY(vertex);
	_DELETE_ARRAY(indices);

	return true;
}

bool Text::m_updatebuffer(const std::wstring &text)
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	HRESULT result = m_render->m_pImmediateContext->Map(m_vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if(FAILED(result))
		return false;

	VertexFont *verticesPtr = (VertexFont*)mappedResource.pData;

	m_font->BuildVertexArray(verticesPtr, m_numvertex, text.c_str());

	m_render->m_pImmediateContext->Unmap(m_vertexBuffer, 0);

	return true;
}

void Text::Draw(float r, float g, float b, float x, float y)
{
	m_RenderBuffers();
	m_font->Draw(m_numdrawindex,r,g,b,x,y);
}

void Text::m_RenderBuffers()
{
	unsigned int stride = sizeof(VertexFont); 
	unsigned int offset = 0;
	m_render->m_pImmediateContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);
	m_render->m_pImmediateContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);
	m_render->m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

void Text::Close()
{
	_RELEASE(m_vertexBuffer);
	_RELEASE(m_indexBuffer);
}

bool Text::SetText(const std::wstring &text)
{
	// статический текст нельзя изменить
	if (m_static)
		return false;
	// если текст меньше чем число индексов, мы не должны рисовать ненужные
	m_numdrawindex = text.size()*6;
	return m_updatebuffer(text);
}

D3D11_Framework.hПравить

#pragma once

#include "macros.h"
#include "stdafx.h"
#include "Framework.h"
#include "InputListener.h"
#include "Render.h"
#include "Shader.h"
#include "Buffer.h"
#include "StaticMesh.h"
#include "Image.h"
#include "BitmapFont.h"
#include "Text.h"

ИсходникиПравить

Исходники - скачать

Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.