Personal Game Engine

2019 年 8 月—至今


项目信息


Starship

Starship 是较早基于该引擎开发的游戏,是经典游戏 Asteroids 的改编版本。开发当时,引擎尚不支持文字与纹理渲染,因此我用简单三角形绘制了所有内容。

游戏中需要击毁所有敌方飞船与小行星才能获胜。

游戏包含 5 个关卡、演示画面和结束画面。


操作方式

调试操作

  • 按 P 暂停
  • 按 T 进入慢速模式
  • 按 O 生成新的小行星
  • 按 F1 切换调试模式
  • 按 F8 强制重启游戏

游戏操作

  • 按上方向键加速飞船
  • 按左/右方向键旋转飞船
  • 按空格键发射子弹
  • 按 N 重生
  • 按 Esc 返回演示模式

Incursion

Incursion 是另一款经典游戏 Battle City

游戏中需要在尽量存活的前提下抵达地图出口才能获胜。玩家可以发射子弹击败敌方坦克与炮塔。

游戏包含 3 个关卡、演示画面、暂停画面和结束画面。


操作方式

调试操作

  • 按 P 暂停
  • 按 T 进入慢速模式(0.1 倍速)
  • 按 Y 进入加速模式(4 倍速)
  • 按 F1 切换调试模式(显示半径/速度)
  • 按 F3 切换“无碰撞”模式,使坦克能够穿过实体地块
  • 按 F4 切换调试相机,显示完整的当前地图
  • 按 F8 强制重启游戏

游戏操作

  • 开始菜单—按 Enter 开始
  • 开始菜单—按 Esc 退出
  • 游戏中—按方向键移动坦克
  • 游戏中—按空格键射击
  • 游戏中—按 P/Esc 暂停
  • 暂停时—按 P 继续
  • 暂停时—按 Esc 返回演示模式
  • 失败/胜利时—按 Esc 返回演示模式
  • 失败/胜利时—若仍有剩余生命,按 P 继续游戏

#include "Engine/Renderer/RenderContext.hpp"
#include "Engine/Math/MathUtils.hpp"
#include "Engine/Core/ErrorWarningAssert.hpp"
#include "Engine/Core/StringUtils.hpp"
#include "Engine/Renderer/Texture.hpp"
#include "Engine/Renderer/TextureView.hpp"
#include "Engine/Renderer/BitmapFont.hpp"
#include "Engine/Math/AABB2.hpp"
#include "Engine/Core/EngineCommon.hpp"
#include "Engine/Core/Rgba8.hpp"
#include "Engine/Math/Disc2.hpp"
#include "Engine/Renderer/Camera.hpp"
#include "Engine/Core/Vertex_PCU.hpp"
#include "Engine/Platform/Window.hpp"
#include "Engine/Renderer/SwapChain.hpp"
#include "Engine/Renderer/Shader.hpp"
#include "Engine/Renderer/VertexBuffer.hpp"
#include "Engine/Renderer/IndexBuffer.hpp"
#include "Engine/Core/ErrorWarningAssert.hpp"
#include "Engine/Core/Time.hpp"
#include "Engine/Renderer/Sampler.hpp"
#include "Engine/Renderer/BuiltinDefaultShader.hpp"
#include "Engine/Renderer/GPUMesh.hpp"
#include "Engine/Math/IntVec2.hpp"
#include "Engine/Renderer/ShaderState.hpp"
#include "Engine/Renderer/Material.hpp"

#include "ThirdParty/stb/stb_image.h"

#include "Engine/Renderer/D3D11Common.hpp"

#pragma comment( lib, "d3d11.lib" )         // needed a01
#pragma comment( lib, "dxgi.lib" )          // needed a01
#pragma comment( lib, "d3dcompiler.lib" )   // needed when we get to shaders


void RenderContext::Startup(Window* window)
{
	// Device - Create Resources
	// Context - Issue Commands

	// ~SwapChain

	// ID3D11Device
	// ID3D11DeviceContext

	IDXGISwapChain* swapChain;

	UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
	#if defined(RENDER_DEBUG)
		flags |= D3D11_CREATE_DEVICE_DEBUG;
	#endif

	// Define the swap chain
	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	memset(&swapChainDesc, 0, sizeof(swapChainDesc));

	// how many back buffers in our chain - we'll double buffer (one we show, one we draw to)
	swapChainDesc.BufferCount = 2;
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // on swap, the old buffer is discarded
	swapChainDesc.Flags = 0; // additional flags - see docs.  Used in special cases like for video buffers

	// how swap chain is to be used
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
	swapChainDesc.OutputWindow = window->GetHandle(); // HWND for the window to be used
	swapChainDesc.SampleDesc.Count = 1; // how many samples per pixel (1 so no MSAA)
										// note, if we're doing MSAA, we'll do it on a secondary target

	// describe the buffer
	swapChainDesc.Windowed = TRUE;                                    // windowed/full-screen mode
	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color RGBA8 color
	swapChainDesc.BufferDesc.Width = window->GetClientWidth();
	swapChainDesc.BufferDesc.Height = window->GetClientHeight();


	HRESULT result = D3D11CreateDeviceAndSwapChain(
		nullptr,
		D3D_DRIVER_TYPE_HARDWARE,
		nullptr,
		flags, // controls the types of device we make
		nullptr,
		0,
		D3D11_SDK_VERSION,
		&swapChainDesc,
		&swapChain,
		&m_device,
		nullptr,
		&m_context );

	GUARANTEE_OR_DIE(SUCCEEDED(result), "Failed");

	m_swapChain = new SwapChain(this, swapChain);

	// Creating Default Shaders
	m_defaultErrorShader = GetOrCreateShaderFromRawCode("DefaultError", gErrorShaderCode);
	m_defaultShader = GetOrCreateShaderFromRawCode("Default", gDefaultShaderCode);
	m_defaultLitShader = GetOrCreateShaderFromRawCode("Lit", gDefaultLitShaderCode);

	// Create VBO
	//m_immediateVBO = new VertexBuffer(this, MEMORY_HINT_DYNAMIC);
	m_immediateMesh = new GPUMesh(this);

	// Create Frame UBO
	m_frameUBO = new RenderBuffer(this, UNIFORM_BUFFER_BIT, MEMORY_HINT_DYNAMIC);

	// Create model matrix UBO
	m_modelMatrixUBO = new RenderBuffer(this, UNIFORM_BUFFER_BIT, MEMORY_HINT_DYNAMIC);

	// Create lighting UBO
	m_sceneUBO = new RenderBuffer(this, UNIFORM_BUFFER_BIT, MEMORY_HINT_DYNAMIC);

	// Create material UBO
	m_materialUBO = new RenderBuffer(this, UNIFORM_BUFFER_BIT, MEMORY_HINT_DYNAMIC);

	// Create default Sampler
	m_defaultSampler = new Sampler(this, SAMPLER_BILINEAR);

	// Create default white texture
	m_defaultTexture = CreateOrGetTextureFromColor(Rgba8::white);
	m_defaultNormalTexture = CreateOrGetTextureFromColor(Rgba8(127, 127, 255, 255));

	// Create Effect Camera
	m_effectCamera = new Camera();
	m_effectCamera->SetClearMode(0, Rgba8::white);

	// Create Blend States
	CreateBlendStates();
	SetBlendMode(BlendMode::BLENDMODE_ALPHA);

	EnableDepth(COMPARE_FUNC_LEQUAL, true);
}

void RenderContext::BeginFrame()
{

}

void RenderContext::EndFrame()
{
	m_swapChain->Present();
}

void RenderContext::Shutdown()
{
	delete m_swapChain;
	m_swapChain = nullptr;

	delete m_defaultShader;
	m_defaultShader = nullptr;

	delete m_defaultErrorShader;
	m_defaultErrorShader = nullptr;

	delete m_defaultLitShader;
	m_defaultLitShader = nullptr;

	delete m_immediateMesh;
	m_immediateMesh = nullptr;

	delete m_frameUBO;
	m_frameUBO = nullptr;

	delete m_modelMatrixUBO;
	m_modelMatrixUBO = nullptr;

	delete m_sceneUBO;
	m_sceneUBO = nullptr;

	delete m_materialUBO;
	m_materialUBO = nullptr;

	if (m_effectCamera != nullptr)
		delete m_effectCamera;


	for (int i = 0; i < m_loadedTextures.size(); i++)
	{
		if (m_loadedTextures[i] != nullptr)
		{
			delete m_loadedTextures[i];
			m_loadedTextures[i] = nullptr;
		}
	}

	GUARANTEE_OR_DIE(totalRenderTargetMade == m_renderTargetPool.size(), "ERROR ON Render Target Pool")
	for (int i = 0; i < m_renderTargetPool.size(); i++)
	{
		if (m_renderTargetPool[i])
		{
			delete m_renderTargetPool[i];
			m_renderTargetPool[i] = nullptr;
		}
	}

	delete m_defaultSampler;
	m_defaultSampler = nullptr;


	DX_SAFE_RELEASE(m_device);
	DX_SAFE_RELEASE(m_context);
	DX_SAFE_RELEASE(m_defaultRasterState);
	DX_SAFE_RELEASE(m_transientRasterState);
	DX_SAFE_RELEASE(m_alphaBlendStateHandle);
	DX_SAFE_RELEASE(m_additiveBlendStateHandle);
	DX_SAFE_RELEASE(m_opaqueBlendStateHandle);
	DX_SAFE_RELEASE(m_currentDepthStencilState);
}


void RenderContext::ClearScreen( Texture* backBuffer, const Rgba8& clearColor )
{
	float clearFloats[4];
	clearColor.GetAsFloats(clearFloats);

	TextureView* backBufferRtv = backBuffer->GetOrCreateRenderTargetView();

	ID3D11RenderTargetView* rtv = backBufferRtv->GetRTVHandle();
	m_context->ClearRenderTargetView(rtv, clearFloats);
}

void RenderContext::ClearScreen(const Rgba8& clearColor)
{
	ClearScreen(GetBackBuffer(), clearColor);
}

void RenderContext::ClearDepth(Texture* depthStencilTexture, float depth, uint stencil)
{
	TextureView* view = depthStencilTexture->GetOrCreateDepthStencilView();

	ID3D11DepthStencilView* dsv = view->GetDSVHandle();
	m_context->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH, depth, (UINT8)stencil);
}

void RenderContext::BeginCamera( const Camera& camera )
{
#if defined(RENDER_DEBUG)
	m_context->ClearState();
	m_lastBoundVBO = nullptr;
#endif


	std::vector colorTargets;
	std::vector rtvs;

	int rtvCount = camera.GetColorTargetCount();
	rtvs.resize(rtvCount);
	colorTargets.resize(rtvCount);

	for (int i = 0; i < rtvCount; i++)
	{
		rtvs[i] = nullptr;
		colorTargets[i] = camera.GetColorTarget(i);

		if (colorTargets[i] != nullptr)
		{
			TextureView* rtv = colorTargets[i]->GetOrCreateRenderTargetView();
			rtvs[i] = rtv->GetRTVHandle();
		}
	}

	if (rtvCount == 0 || colorTargets[0] == nullptr)
	{
		if (rtvCount == 0)
		{
			rtvCount = 1;
			colorTargets.resize(1);
			rtvs.resize(1);
		}
		colorTargets[0] = GetBackBuffer();
		rtvs[0] = GetBackBuffer()->GetOrCreateRenderTargetView()->GetRTVHandle();
	}


	if (camera.m_clearMode & CLEAR_COLOR_BIT)
	{
		ClearScreen(colorTargets[0], camera.GetClearColor());
		for (int i = 1; i < rtvCount; i++)
			ClearScreen(colorTargets[i], Rgba8::black);
	}


	m_allowsDrawing = true;



	ID3D11DepthStencilView* dsv = nullptr;
	if (camera.m_depthStencilTarget)
	{
		TextureView* psdv = camera.m_depthStencilTarget->GetOrCreateDepthStencilView();
		dsv = psdv->GetDSVHandle();

		if (camera.m_clearMode & CLEAR_DEPTH_BIT)
			ClearDepth(camera.m_depthStencilTarget, camera.m_clearDepth, camera.m_clearStencil);
	}


	m_context->OMSetRenderTargets(rtvCount, rtvs.data(), dsv);

	D3D11_VIEWPORT viewport;
	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	viewport.Width = (FLOAT)colorTargets[0]->GetWidth();
	viewport.Height = (FLOAT)colorTargets[0]->GetHeight();
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;


	// Reset every frame
	m_cullMode = CULLMODE_NONE;
	m_fillMode = FILLMODE_SOLID;
	m_frontFaceWindOrder = FRONT_FACE_WIND_COUNTERCLOCKWISE;


	m_context->RSSetViewports(1, &viewport);

	BindShader(nullptr);
	BindSampler(nullptr);
	BindTexture(nullptr);
	BindNormalTexture(nullptr);
	UpdateRasterState(true);

	if (camera.m_cameraUBO == nullptr)
	{
		camera.CreateCameraUBO(this);
	}

	BindUniformBuffer(UBO_FRAME_SLOT, m_frameUBO);
	BindUniformBuffer(UBO_CAMERA_SLOT, camera.m_cameraUBO);
	BindUniformBuffer(UBO_MODEL_SLOT, m_modelMatrixUBO);
	BindUniformBuffer(UBO_LIGHT_SLOT, m_sceneUBO);
	BindUniformBuffer(UBO_MATERIAL_SLOT, m_materialUBO);

	m_currentModel = model_data_t();
	SetModelMatrix(Mat44::IDENTITY);
}

void RenderContext::EndCamera( const Camera& camera )
{
	UNUSED(camera);
	DX_SAFE_RELEASE(m_currentDepthStencilState);
	m_allowsDrawing = false;
}

void RenderContext::Draw(int numVertexes, int vertexOffset)
{
	// TEMP FUNCTION
	// Set up the GPU for a draw


	// Describe the vertex format to the shader
	//ID3D11InputLayout* inputLayout = m_currentShader->GetOrCreateInputLayout(m_immediateVBO->m_layout);
	//m_context->IASetInputLayout(inputLayout);

	m_context->Draw(numVertexes, vertexOffset);


}


void RenderContext::DrawIndexed(int numVertexes, int startIndexLocation, int baseVertexLocation)
{
	m_context->DrawIndexed(numVertexes, startIndexLocation, baseVertexLocation);
}

void RenderContext::DrawMesh(GPUMesh* mesh)
{
	if (mesh->GetVertexCount() == 0)
		return;

	BindVertexInput(mesh->m_vertexBuffer);

	//UpdateInputLayout(mesh->m_vertexBuffer->m_layout);
	ID3D11InputLayout* inputLayout = m_currentShader->GetOrCreateInputLayout(mesh->m_vertexBuffer->m_layout);
	m_context->IASetInputLayout(inputLayout);

	bool hasIndicies = mesh->GetIndexCount() > 0;

	if (hasIndicies)
	{
		BindIndexInput(mesh->m_indexBuffer);
		DrawIndexed(mesh->GetIndexCount(), 0, 0);
	}
	else {
		Draw(mesh->GetVertexCount());
	}
}

void RenderContext::DrawVertexArray( int numVertexes, const Vertex_PCU* vertexes )
{
	// start drawing the triangles from the vertex array
	// reminder for check if numVertexes is a multiple of 3

	// Update a vertex buffer
	// size_t bufferByteSize = numVertexes * sizeof(Vertex_PCU);
	// size_t elementSize = sizeof(Vertex_PCU);

	//m_immediateVBO->Update(vertexes, sizeof(Vertex_PCU), numVertexes, Vertex_PCU::LAYOUT);

	// Bind
	//BindVertexInput(m_immediateVBO);

	// IndexInput
	// BindIndexInput(m_immediateIBO);

	// Draw
	//Draw(numVertexes, 0);

	m_immediateMesh->UpdateVerticies(numVertexes, vertexes);
	m_immediateMesh->UpdateIndices(0, nullptr);

	DrawMesh(m_immediateMesh);
}

void RenderContext::DrawVertexArray(const std::vector& vertexArray)
{
	if (vertexArray.size() == 0) return;
	DrawVertexArray((int)vertexArray.size(), &vertexArray[0]);
}

void RenderContext::DrawAABB2D(const AABB2& aabb2D, const Rgba8& color)
{
	Vertex_PCU vertexes[6];
	vertexes[0] = Vertex_PCU(Vec3(aabb2D.mins.x, aabb2D.mins.y), color, Vec2(0.f, 0.f));
	vertexes[1] = Vertex_PCU(Vec3(aabb2D.maxs.x, aabb2D.mins.y), color, Vec2(1.f, 0.f));
	vertexes[2] = Vertex_PCU(Vec3(aabb2D.mins.x, aabb2D.maxs.y), color, Vec2(0.f, 1.f));

	vertexes[3] = Vertex_PCU(Vec3(aabb2D.mins.x, aabb2D.maxs.y), color, Vec2(0.f, 1.f));
	vertexes[4] = Vertex_PCU(Vec3(aabb2D.maxs.x, aabb2D.mins.y), color, Vec2(1.f, 0.f));
	vertexes[5] = Vertex_PCU(Vec3(aabb2D.maxs.x, aabb2D.maxs.y), color, Vec2(1.f, 1.f));
	DrawVertexArray(6, vertexes);

}

void RenderContext::DrawLine(const Vec2& start, const Vec2& end, const Rgba8& color, float thickness)
{
	Vec2 displacement = end - start;
	float r = thickness * .5f;

	Vec2 forwardVector = displacement.GetNormalized();
	forwardVector.SetLength(r);
	Vec2 leftVector = forwardVector.GetRotated90Degrees();

	Vec2 endLeft = end + forwardVector + leftVector;
	Vec2 endRight = end + forwardVector - leftVector;
	Vec2 startLeft = start - forwardVector + leftVector;
	Vec2 startRight = start - forwardVector - leftVector;

	Vertex_PCU vertexes[] =
	{
		Vertex_PCU(Vec3(startRight),	color, Vec2::Zero()),
		Vertex_PCU(Vec3(endRight),		color, Vec2::Zero()),
		Vertex_PCU(Vec3(endLeft),		color, Vec2::Zero()),
		Vertex_PCU(Vec3(startRight),	color, Vec2::Zero()),
		Vertex_PCU(Vec3(endLeft),		color, Vec2::Zero()),
		Vertex_PCU(Vec3(startLeft),		color, Vec2::Zero()),
	};

	const int NUM_VERTEXES = sizeof(vertexes) / sizeof(vertexes[0]);

	DrawVertexArray(NUM_VERTEXES, vertexes);
}

void RenderContext::DrawDisc2D(const Disc2& disc2D, const Rgba8& color)
{
	Vec2 center = disc2D.m_center;
	float radius = disc2D.m_radius;

	std::vector vertexes;
	const int NUM_TRIANGLES = 32;
	const float intervalAngle = 360.f / NUM_TRIANGLES;
	for (int triIndex = 0; triIndex < NUM_TRIANGLES; triIndex++)
	{
		Vec2 p1 = center + Vec2::MakeFromPolarDegrees(triIndex * intervalAngle, radius);
		Vec2 p2 = center + Vec2::MakeFromPolarDegrees((triIndex + 1) * intervalAngle, radius);
		vertexes.push_back(Vertex_PCU(center, color, Vec2::Zero()));
		vertexes.push_back(Vertex_PCU(p1, color, Vec2::Zero()));
		vertexes.push_back(Vertex_PCU(p2, color, Vec2::Zero()));
	}

	DrawVertexArray(vertexes);
}

void RenderContext::DrawRing2D(const Disc2& disc2D, const Rgba8& color, float thickness)
{
	Vec2 center = disc2D.m_center;
	float radius = disc2D.m_radius;

	const int NUM_TRIANGLES = 64;
	const float intervalAngle = 360.f / NUM_TRIANGLES;
	for (int triIndex = 0; triIndex < NUM_TRIANGLES; triIndex++)
	{
		Vec2 start = center + Vec2::MakeFromPolarDegrees(triIndex * intervalAngle, radius);
		Vec2 end = center + Vec2::MakeFromPolarDegrees((triIndex + 1) * intervalAngle, radius);
		DrawLine(start, end, color, thickness);
	}
}

Texture* RenderContext::GetBackBuffer() const
{
	return m_swapChain->GetBackBuffer();
}

Texture* RenderContext::CreateOrGetTextureFromFile(const char* imageFilePath)
{
	// check if we have already created the texture first
	for (int texIdx = 0; texIdx < m_loadedTextures.size(); texIdx++)
	{
		if (m_loadedTextures[texIdx]->GetFilePath() == imageFilePath) {
			return m_loadedTextures[texIdx];
		}
	}

	// return a new texture
	return CreateTextureFromFile(imageFilePath);
}

Texture* RenderContext::CreateOrGetTextureFromColor(const Rgba8& color, IntVec2 size)
{
	for (int texIdx = 0; texIdx < m_loadedTextures.size(); texIdx++)
	{
		if (m_loadedTextures[texIdx]->GetFilePath() == Stringf("%s %s", color.GetString().c_str(), size.GetString().c_str())) {
			return m_loadedTextures[texIdx];
		}
	}

	// return a new texture
	return CreateTextureFromColor(color, size);
}

void RenderContext::BindTexture( const Texture* constTex)
{
	Texture* tex = nullptr;
	if (constTex == nullptr)
	{
		tex = m_defaultTexture;
	}
	else
	{
		tex = const_cast(constTex);
	}

	TextureView* shaderResourceView = tex->GetOrCreateShaderResourceView();
	ID3D11ShaderResourceView* srvHandle = shaderResourceView->GetSRVHandle();
	m_context->PSSetShaderResources(0, 1, &srvHandle);

	SetBlendMode(BlendMode::BLENDMODE_ALPHA);

}

void RenderContext::BindTexture(std::string fileName)
{
	BindTextureToSlotByName(fileName.c_str(), 0);
}

void RenderContext::BindTextureToSlot(const Texture* constTex, uint slot)
{
	Texture* tex = nullptr;
	if (constTex == nullptr)
	{
		tex = m_defaultTexture;
	}
	else
	{
		tex = const_cast(constTex);
	}

	TextureView* shaderResourceView = tex->GetOrCreateShaderResourceView();
	ID3D11ShaderResourceView* srvHandle = shaderResourceView->GetSRVHandle();
	m_context->PSSetShaderResources(slot, 1, &srvHandle);

	SetBlendMode(BlendMode::BLENDMODE_ALPHA);
}

void RenderContext::BindNormalTexture(const Texture* normalTexture)
{
	Texture* tex = nullptr;
	if (normalTexture == nullptr)
	{
		tex = m_defaultNormalTexture;
	}
	else
	{
		tex = const_cast(normalTexture);
	}

	TextureView* shaderResourceView = tex->GetOrCreateShaderResourceView();
	ID3D11ShaderResourceView* srvHandle = shaderResourceView->GetSRVHandle();
	m_context->PSSetShaderResources(1, 1, &srvHandle);

	SetBlendMode(BlendMode::BLENDMODE_ALPHA);
}

bool RenderContext::BindDiffuseTextureByName(char const* fileName)
{
	Texture* tex = CreateOrGetTextureFromFile(fileName);
	BindTexture(tex);
	if (tex == nullptr) {
		return false;
	}
	else {
		return true;
	}
}

bool RenderContext::BindNormalTextureByName(char const* fileName)
{
	Texture* tex = CreateOrGetTextureFromFile(fileName);
	BindNormalTexture(tex);
	if (tex == nullptr) {
		return false;
	}
	else {
		return true;
	}
}

bool RenderContext::BindTextureToSlotByName(char const* fileName, uint slot)
{
	Texture* tex = CreateOrGetTextureFromFile(fileName);
	BindTextureToSlot(tex, slot);
	if (tex == nullptr) {
		return false;
	}
	else {
		return true;
	}
}

void RenderContext::BindSampler(Sampler* sampler)
{
	ID3D11SamplerState* handle = nullptr;
	if (nullptr == sampler)
	{
		handle = m_defaultSampler->GetHandle();
	}
	else
	{
		handle = sampler->GetHandle();
	}

	m_context->PSSetSamplers(0, 1, &handle);
}

void RenderContext::BindSamplerToSolot(Sampler* sampler, uint slot)
{
	ID3D11SamplerState* handle = nullptr;
	if (nullptr == sampler)
	{
		handle = m_defaultSampler->GetHandle();
	}
	else
	{
		handle = sampler->GetHandle();
	}

	m_context->PSSetSamplers(slot, 1, &handle);
}

Texture* RenderContext::CreateTextureFromColor(const Rgba8& color, IntVec2 size)
{
	// make a 1x1 texture of that color;
	int dataSize = 4 * size.x * size.y;
	unsigned char* imageData = new unsigned char[dataSize];

	for (int i = 0; i < size.x * size.y; i++)
	{
		imageData[i * 4] = color.r;
		imageData[i * 4 + 1] = color.g;
		imageData[i * 4 + 2] = color.b;
		imageData[i * 4 + 3] = color.a;
	}

	D3D11_TEXTURE2D_DESC desc;
	D3D11_SUBRESOURCE_DATA initialData;

	desc.Width = (unsigned int)size.x;
	desc.Height = (unsigned int)size.y;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	desc.SampleDesc.Count = 1; // MSAA
	desc.SampleDesc.Quality = 0;

	desc.Usage = D3D11_USAGE_IMMUTABLE; // mip-chains : GPU/DEFAULT
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;     // For extension feature


	initialData.pSysMem = imageData;
	initialData.SysMemPitch = desc.Width * 4;
	initialData.SysMemSlicePitch = 0;

	ID3D11Texture2D* texHandle = nullptr;
	m_device->CreateTexture2D(&desc, &initialData, &texHandle);

	Texture* newTex = new Texture(this, texHandle, Stringf("%s %s", color.GetString().c_str(), size.GetString().c_str()));
	m_loadedTextures.push_back(newTex);

	return newTex;

}

Texture* RenderContext::CreateDepthTexture(const IntVec2& outputSize)
{
	D3D11_TEXTURE2D_DESC desc;

	desc.Width = (UINT)outputSize.x;
	desc.Height = (UINT)outputSize.y;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_D32_FLOAT;

	desc.SampleDesc.Count = 1; // MSAA
	desc.SampleDesc.Quality = 0;

	desc.Usage = D3D11_USAGE_DEFAULT; // mip-chains : GPU/DEFAULT
	desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;     // For extension feature


	ID3D11Texture2D* texHandle = nullptr;
	m_device->CreateTexture2D(&desc, NULL, &texHandle);


	Texture* newTex = new Texture(this, texHandle, "DepthTexure");
	m_loadedTextures.push_back(newTex);

	return newTex;
}

Texture* RenderContext::CreateRenderTarget(const IntVec2& outputSize)
{
	D3D11_TEXTURE2D_DESC desc;

	desc.Width = (UINT)outputSize.x;
	desc.Height = (UINT)outputSize.y;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	desc.SampleDesc.Count = 1; // MSAA
	desc.SampleDesc.Quality = 0;

	desc.Usage = D3D11_USAGE_DEFAULT; // mip-chains : GPU/DEFAULT
	desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;     // For extension feature


	ID3D11Texture2D* texHandle = nullptr;
	m_device->CreateTexture2D(&desc, NULL, &texHandle);

	// We are not going to delete it
	Texture* newTex = new Texture(this, texHandle, "RenderTarget");

	return newTex;
}

Texture* RenderContext::AcquireRenderTargetMatching(Texture* texture)
{
	IntVec2 size = texture->GetTexelDimensions();
	for (int i = 0; i < m_renderTargetPool.size(); i++)
	{
		Texture* rt = m_renderTargetPool[i];
		if (rt->GetTexelDimensions() == size)
		{
			m_renderTargetPool[i] = m_renderTargetPool[m_renderTargetPool.size() - 1];
			m_renderTargetPool.pop_back();

			return rt;
		}
	}

	// If nothing in the pool matches

	Texture* newTexture = CreateRenderTarget(size);
	totalRenderTargetMade++;
	return newTexture;
}

void RenderContext::ReleaseRenderTarget(Texture* tex)
{
	m_renderTargetPool.push_back(tex);
}

int RenderContext::GetRenderTargetPoolFreeCount()
{
	return (int)m_renderTargetPool.size();
}

void RenderContext::CopyTexture(Texture* dst, Texture* src)
{
	m_context->CopyResource(dst->m_handle, src->m_handle);
}

void RenderContext::StartEffect(Texture* dst, Texture* src, Shader* shader)
{
	m_effectCamera->SetColorTarget(dst);
	BeginCamera(*m_effectCamera);
	BindShader(shader);
	BindTexture(src);
}

void RenderContext::EndEffect()
{
	m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	m_context->Draw(3, 0);
	EndCamera(*m_effectCamera);
}

void RenderContext::ApplyEffect(Texture* dst, Texture* src, Material* mat)
{
	m_effectCamera->SetColorTarget(dst);
	BeginCamera(*m_effectCamera);
	BindShader(mat->m_shaderState->m_shader);
	BindMaterial(mat);
	BindTexture(src);
	m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	m_context->Draw(3, 0);
	EndCamera(*m_effectCamera);
}

void RenderContext::SetBlendMode(BlendMode blendMode)
{
	float const zeroes[] = { 0.f, 0.f, 0.f, 0.f };
	switch (blendMode)
	{
	case BlendMode::BLENDMODE_ALPHA:
		m_context->OMSetBlendState(m_alphaBlendStateHandle, zeroes, (UINT)~0);
		break;
	case BlendMode::BLENDMODE_ADDITIVE:
		m_context->OMSetBlendState(m_additiveBlendStateHandle, zeroes, (UINT)~0);
		break;
	case BlendMode::BLENDMODE_OPAQUE:
	default:
		m_context->OMSetBlendState(m_opaqueBlendStateHandle, zeroes, (UINT)~0);
		break;
	}


}

void RenderContext::CreateBlendStates()
{

	D3D11_BLEND_DESC alphaDesc, additiveDesc, opaqueDesc;
	alphaDesc.AlphaToCoverageEnable = false;
	alphaDesc.IndependentBlendEnable = false;

	alphaDesc.RenderTarget[0].BlendEnable = true;
	alphaDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	alphaDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	alphaDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;

	alphaDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	alphaDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	alphaDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;

	alphaDesc.RenderTarget[0].RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;

	m_device->CreateBlendState(&alphaDesc, &m_alphaBlendStateHandle);



	additiveDesc.AlphaToCoverageEnable = false;
	additiveDesc.IndependentBlendEnable = false;

	additiveDesc.RenderTarget[0].BlendEnable = true;
	additiveDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	//additiveDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	additiveDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	additiveDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;

	additiveDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	additiveDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	additiveDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;

	additiveDesc.RenderTarget[0].RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;


	m_device->CreateBlendState(&additiveDesc, &m_additiveBlendStateHandle);


	opaqueDesc.AlphaToCoverageEnable = false;
	opaqueDesc.IndependentBlendEnable = false;

	opaqueDesc.RenderTarget[0].BlendEnable = true;
	opaqueDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	opaqueDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	opaqueDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;

	opaqueDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	opaqueDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	opaqueDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;

	opaqueDesc.RenderTarget[0].RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL;


	m_device->CreateBlendState(&opaqueDesc, &m_opaqueBlendStateHandle);
}

void RenderContext::SetModelMatrix(Mat44 mat)
{
	m_currentModel.modelMat = mat;

	m_modelMatrixUBO->Update(&m_currentModel, sizeof(model_data_t), sizeof(model_data_t));

}

void RenderContext::SetModelTint(Vec4 tint)
{
	m_currentModel.tint = tint;

	m_modelMatrixUBO->Update(&m_currentModel, sizeof(model_data_t), sizeof(model_data_t));
}

void RenderContext::SetModelTint(Rgba8 tint)
{
	SetModelTint(tint.GetAsFloats());
}

void RenderContext::SetSpecularFactor(float specularFactor)
{
	m_currentModel.m_specularFactor = specularFactor;
	m_modelMatrixUBO->Update(&m_currentModel, sizeof(model_data_t), sizeof(model_data_t));
}

void RenderContext::SetSpecularPower(float specularPower)
{
	m_currentModel.m_specularPower = specularPower;
	m_modelMatrixUBO->Update(&m_currentModel, sizeof(model_data_t), sizeof(model_data_t));
}

BitmapFont* RenderContext::CreateOrGetBitmapFont(const char* bitmapFontFilePathNoExtension)
{
	for (int fontIdx = 0; fontIdx < m_loadedFonts.size(); fontIdx++)
	{
		if (m_loadedFonts[fontIdx]->m_fontName == bitmapFontFilePathNoExtension)
			return m_loadedFonts[fontIdx];
	}
	return CreateBitmapFontFromFile(bitmapFontFilePathNoExtension);
}

void RenderContext::BindShader(Shader* shader)
{
	ASSERT_OR_DIE(IsDrawing(), "Not having a camera while binding the shader!"); // Do I have a camera?

	m_currentShader = shader;
	if (nullptr == m_currentShader)
	{
		m_currentShader = m_defaultShader;
	}
	else {
		if (m_currentShader->isErrorShader)
		{
			m_currentShader = m_defaultErrorShader;
		}
	}

	m_context->VSSetShader(m_currentShader->m_vertexStage.m_vs, nullptr, 0);
	m_context->PSSetShader(m_currentShader->m_fragementStage.m_fs, nullptr, 0);
}

void RenderContext::BindShader(std::string const& fileName)
{
	Shader* shader = GetOrCreateShader(fileName);
	BindShader(shader);
}

void RenderContext::UpdateRasterState(bool enableDefaultSettings)
{
	// Use Default Raster State
	if (enableDefaultSettings)
	{
		// Create default raster state
		if (!m_defaultRasterState)
		{
			D3D11_RASTERIZER_DESC desc;
			desc.FillMode = D3D11_FILL_SOLID;
			desc.CullMode = D3D11_CULL_NONE;
			desc.FrontCounterClockwise = TRUE;
			desc.DepthBias = 0U;
			desc.DepthBiasClamp = 0.0f;
			desc.SlopeScaledDepthBias = 0.0f;
			desc.DepthClipEnable = TRUE;
			desc.ScissorEnable = FALSE;
			desc.MultisampleEnable = FALSE;
			desc.AntialiasedLineEnable = FALSE;

			m_device->CreateRasterizerState(&desc, &m_defaultRasterState);
		}

		m_context->RSSetState(m_defaultRasterState);
		return;
	}

	if (m_transientRasterState)
	{
		DX_SAFE_RELEASE(m_transientRasterState);
	}

	CullMode newCullMode = m_cullMode;
	FillMode newFillMode = m_fillMode;
	FrontFaceWindOrder newWindOrder = m_frontFaceWindOrder;
	if (enableDefaultSettings)
	{
		newCullMode = CULLMODE_NONE;
		newFillMode = FILLMODE_SOLID;
		newWindOrder = FRONT_FACE_WIND_COUNTERCLOCKWISE;
	}

	D3D11_RASTERIZER_DESC desc;

	switch (newFillMode)
	{
	case FILLMODE_SOLID:
		desc.FillMode = D3D11_FILL_SOLID;
		break;
	case FILLMODE_WIREFRAME:
		desc.FillMode = D3D11_FILL_WIREFRAME;
		break;
	default:
		desc.FillMode = D3D11_FILL_SOLID;
		break;
	}

	switch (newCullMode)
	{
	case CULLMODE_NONE:
		desc.CullMode = D3D11_CULL_NONE;
		break;
	case CULLMODE_FRONT:
		desc.CullMode = D3D11_CULL_FRONT;
		break;
	case CULLMODE_BACK:
		desc.CullMode = D3D11_CULL_BACK;
		break;
	default:
		desc.CullMode = D3D11_CULL_NONE;
		break;
	}

	switch (newWindOrder)
	{
	case FRONT_FACE_WIND_CLOCKWISE:
		desc.FrontCounterClockwise = FALSE;
		break;
	case FRONT_FACE_WIND_COUNTERCLOCKWISE:
		desc.FrontCounterClockwise = TRUE;
		break;
	default:
		desc.FrontCounterClockwise = TRUE;
		break;
	}

	desc.DepthBias = 0U;
	desc.DepthBiasClamp = 0.0f;
	desc.SlopeScaledDepthBias = 0.0f;
	desc.DepthClipEnable = TRUE;
	desc.ScissorEnable = FALSE;
	desc.MultisampleEnable = FALSE;
	desc.AntialiasedLineEnable = FALSE;

	m_device->CreateRasterizerState(&desc, &m_transientRasterState);
	m_context->RSSetState(m_transientRasterState);
}

void RenderContext::BindVertexInput(VertexBuffer* vbo)
{
	ID3D11Buffer* vboHandle = vbo->m_handle;
	UINT stride = (UINT)vbo->m_stride; // how far from one vertex to next
	UINT offset = 0; // how far into buffer we start


	if (m_lastBoundVBO != vboHandle)
	{
		m_context->IASetVertexBuffers(0, 1, &vboHandle, &stride, &offset);
		m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		m_lastBoundVBO = vboHandle;
	}
}

void RenderContext::BindIndexInput(IndexBuffer* ibo)
{
	ID3D11Buffer* iboHandle = ibo->m_handle;

	m_context->IASetIndexBuffer(iboHandle, DXGI_FORMAT_R32_UINT, 0);

}

void RenderContext::BindUniformBuffer(uint slot, RenderBuffer* ubo)
{
	ID3D11Buffer* uboHandle = ubo->m_handle;

	m_context->VSSetConstantBuffers(slot, 1, &uboHandle);
	m_context->PSSetConstantBuffers(slot, 1, &uboHandle);
}

void RenderContext::EnableDepth(CompareFunc func, bool writeDepthOnPass)
{
	if (m_currentDepthStencilState != nullptr)
	{
		DX_SAFE_RELEASE(m_currentDepthStencilState);
	}

	D3D11_DEPTH_STENCIL_DESC desc;
	memset(&desc, 0, sizeof(desc));

	switch (func) {
	case COMPARE_FUNC_ALWAYS:
		desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
		break;
	case COMPARE_FUNC_LEQUAL:
		desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
		break;
	case COMPARE_FUNC_GEQUAL:
		desc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL;
		break;
	case COMPARE_FUNC_LESS:
		desc.DepthFunc = D3D11_COMPARISON_LESS;
		break;
	case COMPARE_FUNC_GREATER:
		desc.DepthFunc = D3D11_COMPARISON_GREATER;
		break;
	case COMPARE_FUNC_NEVER:
		desc.DepthFunc = D3D11_COMPARISON_NEVER;
		break;
	case COMPARE_FUNC_EQUAL:
		desc.DepthFunc = D3D11_COMPARISON_EQUAL;
		break;
	}
	desc.DepthEnable = true;
	if (writeDepthOnPass)
		desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	else
		desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;



	// TODO release old one

	m_device->CreateDepthStencilState(&desc, &m_currentDepthStencilState);
	m_context->OMSetDepthStencilState(m_currentDepthStencilState, 0);
}

void RenderContext::DisableDepth()
{
	EnableDepth(COMPARE_FUNC_ALWAYS, false);
}

void RenderContext::EnableLight(uint idx, light_t const& lightInfo)
{
	// idx for multiple lights use later
	UNUSED(idx);

	m_currentSceneVariables.m_lights[idx] = lightInfo;
	m_sceneUBO->Update(&m_currentSceneVariables, sizeof(scene_data_t), sizeof(scene_data_t));
}

void RenderContext::DisableLight(uint idx)
{
	UNUSED(idx)

	for (int i = 0; i < MAX_LIGHTS; i++)
		m_currentSceneVariables.m_lights[i] = light_t();
	BindShader(nullptr);
}

void RenderContext::SetLightAmbient(Rgba8 color)
{
	Vec4 ambient = color.GetAsFloats();
	ambient.w = m_currentSceneVariables.m_ambient.w;
	m_currentSceneVariables.m_ambient = ambient;

	m_sceneUBO->Update(&m_currentSceneVariables, sizeof(scene_data_t), sizeof(scene_data_t));
}

void RenderContext::SetLightIntensity(float intensity)
{
	m_currentSceneVariables.m_ambient.w = intensity;

	m_sceneUBO->Update(&m_currentSceneVariables, sizeof(scene_data_t), sizeof(scene_data_t));
}

void RenderContext::SetLightAmbientIntensity(Rgba8 color, float intensity)
{
	Vec4 ambient = color.GetAsFloats();
	ambient.w = intensity;
	m_currentSceneVariables.m_ambient = ambient;

	m_sceneUBO->Update(&m_currentSceneVariables, sizeof(scene_data_t), sizeof(scene_data_t));
}

void RenderContext::EnableFog(float nearFogDistance, float farFogDistance, Rgba8 nearFogColor, Rgba8 farFogColor)
{
	m_currentSceneVariables.m_nearFog = nearFogDistance;
	m_currentSceneVariables.m_farFog = farFogDistance;
	m_currentSceneVariables.m_nearFogColor = nearFogColor.GetAsFloats();
	m_currentSceneVariables.m_farFogColor = farFogColor.GetAsFloats();

	m_sceneUBO->Update(&m_currentSceneVariables, sizeof(scene_data_t), sizeof(scene_data_t));
}

void RenderContext::DisableFog()
{
	EnableFog(1000000.f, 1000000.f, Rgba8::white, Rgba8(255, 255, 255, 0));
}

void RenderContext::SetMaterial(const void* data, size_t bufferByteSize, size_t elementByteSize)
{
	m_materialUBO->Update(data, bufferByteSize, elementByteSize);
	BindUniformBuffer(UBO_MATERIAL_SLOT, m_materialUBO);
}

Shader* RenderContext::GetOrCreateShader(std::string const& fileName)
{
	for (int i = 0; i < m_shaders.size(); i++)
	{
		if (m_shaders[i]->m_fileName == fileName)
			return m_shaders[i];
	}

	Shader* s = new Shader(this);
	s->CreateFromFile(fileName);
	m_shaders.push_back(s);

	return s;

}

Shader* RenderContext::GetOrCreateShaderFromRawCode(std::string const& fileName, char const* context)
{
	for (int i = 0; i < m_shaders.size(); i++)
	{
		if (m_shaders[i]->m_fileName == fileName)
			return m_shaders[i];
	}

	Shader* s = new Shader(this);
	s->CreateFromContext(fileName, context);
	m_shaders.push_back(s);

	return s;
}

void RenderContext::BindShaderState(ShaderState* shaderState)
{
	BindShader(shaderState->m_shader);
	SetBlendMode(shaderState->m_blendMode);
	EnableDepth(shaderState->m_depthTest, shaderState->m_writeDepth);
	SetFrontFaceWindOrder(shaderState->m_windingOrder);
	SetCullMode(shaderState->m_cullMode);
	SetFillMode(shaderState->m_fillMode);
}

void RenderContext::BindShaderState(std::string const& shaderStateName)
{
	for (int i = 0; i < m_shaderStates.size(); i++)
		if (m_shaderStates[i]->m_shaderStateName == shaderStateName)
		{
			BindShaderState(m_shaderStates[i]);
			return;
		}
	//g_theConsole->PrintString(Rgba8::yellow, Stringf("Try to bind unknown Shader State %s", shaderStateName.c_str()));

}

void RenderContext::CreateShaderStatesFromFile(std::string const& fileName)
{
	MyXMLDocument xmlDocument;
	xmlDocument.LoadFile(fileName.c_str());

	if (xmlDocument.ErrorID() != tinyxml2::XML_SUCCESS) {
		ERROR_AND_DIE("ShaderState: XML Document parse failed!")
	}

	XMLElement* rootElement = xmlDocument.RootElement();
	if (!rootElement) {
		ERROR_AND_DIE("ShaderState: XML Document parse failed!")
	}

	for (XMLElement* currentElement = rootElement; currentElement != NULL; currentElement = currentElement->NextSiblingElement())
	{
		if (Stricmp(currentElement->Name(), "ShaderState") == 0)
		{
			GetOrCreateShaderState(*currentElement);
		}
	}

}

ShaderState* RenderContext::GetOrCreateShaderState(const XMLElement& element)
{
	ShaderState* newShaderState = new ShaderState(this, element);

	for (int i = 0; i < m_shaderStates.size(); i++)
		if (m_shaderStates[i]->m_shaderStateName == newShaderState->m_shaderStateName)
			return m_shaderStates[i];

	m_shaderStates.push_back(newShaderState);
	return newShaderState;
}

ShaderState* RenderContext::GetOrCreateShaderState(std::string const& shaderStateName)
{
	for (int i = 0; i < m_shaderStates.size(); i++)
		if (m_shaderStates[i]->m_shaderStateName == shaderStateName)
		{
			return m_shaderStates[i];
		}
	//g_theConsole->PrintString(Rgba8::red, Stringf("Try to find Shader State %s", shaderStateName.c_str()));

	return nullptr;
}

void RenderContext::BindMaterial(Material* material)
{
	BindShaderState(material->m_shaderState);

	for (uint i = 0; i < (uint)(material->m_texturesPerSlot.size()); i++)
		if (material->m_texturesPerSlot[i] != nullptr)
			BindTextureToSlot(material->m_texturesPerSlot[i], i);

	for (uint i = 0; i < (uint)(material->m_samplersPerSlot.size()); i++)
		if (material->m_samplersPerSlot[i] != nullptr)
			BindSamplerToSolot(material->m_samplersPerSlot[i], i);

	material->UpdateUBOIfDirty();
}

void RenderContext::SetCullMode(CullMode newCullMode)
{
	if (newCullMode == m_cullMode)
		return;
	m_cullMode = newCullMode;
	UpdateRasterState();
}

void RenderContext::SetFillMode(FillMode newFillMode)
{
	if (newFillMode == m_fillMode)
		return;
	m_fillMode = newFillMode;
	UpdateRasterState();
}

void RenderContext::SetFrontFaceWindOrder(FrontFaceWindOrder newOrder)
{
	if (m_frontFaceWindOrder == newOrder)
		return;
	m_frontFaceWindOrder = newOrder;
	UpdateRasterState();
}

Texture* RenderContext::GetFrameColorTarget()
{
	return m_swapChain->GetBackBuffer();
}

void RenderContext::UpdateFrameTime(float deltaSeconds)
{
	frame_data_t frameData;
	frameData.system_time = (float)GetCurrentTimeSeconds();
	frameData.system_delta_time = deltaSeconds;

	m_frameUBO->Update(&frameData, sizeof(frameData), sizeof(frameData));
}

BitmapFont* RenderContext::CreateBitmapFontFromFile(const char* fontName, const char* fontFilePath)
{
	std::string fontFilePathString = fontFilePath;
	fontFilePathString.append(".png");
	Texture* fontTex = CreateOrGetTextureFromFile(fontFilePathString.data());
	return new BitmapFont(fontName, fontTex);
}

BitmapFont* RenderContext::CreateBitmapFontFromFile(const char* fontFilePath)
{
	const char* fontName = fontFilePath;
	return CreateBitmapFontFromFile(fontName, fontFilePath);
}

Texture* RenderContext::CreateTextureFromFile(const char* imageFilePath)
{
	//unsigned int textureID = 0;
	int imageTexelSizeX = 0; // This will be filled in for us to indicate image width
	int imageTexelSizeY = 0; // This will be filled in for us to indicate image height
	int numComponents = 0; // This will be filled in for us to indicate how many color components the image had (e.g. 3=RGB=24bit, 4=RGBA=32bit)
	int numComponentsRequested = 0; // don't care; we support 3 (24-bit RGB) or 4 (32-bit RGBA)

	// Load (and decompress) the image RGB(A) bytes from a file on disk into a memory buffer (array of bytes)
	stbi_set_flip_vertically_on_load(1); // We prefer uvTexCoords has origin (0,0) at BOTTOM LEFT
	unsigned char* imageData = stbi_load(imageFilePath, &imageTexelSizeX, &imageTexelSizeY, &numComponents, numComponentsRequested);

	// Check if the load was successful
	GUARANTEE_OR_DIE(imageData, Stringf("Failed to load image \"%s\"", imageFilePath));
	GUARANTEE_OR_DIE(numComponents >= 3 && numComponents <= 4 && imageTexelSizeX > 0 && imageTexelSizeY > 0, Stringf("ERROR loading image \"%s\" (Bpp=%i, size=%i,%i)", imageFilePath, numComponents, imageTexelSizeX, imageTexelSizeY));

	// if numComponents == 3, we create new data
	if (numComponents == 3)
	{
		int tempDataSize = imageTexelSizeX * imageTexelSizeY * 4;
		unsigned char* tempData = new unsigned char[tempDataSize];
		for (int i = 0; i < imageTexelSizeX * imageTexelSizeY; i++)
		{
			tempData[i * 4] = imageData[i * 3];
			tempData[i * 4 + 1] = imageData[i * 3 + 1];
			tempData[i * 4 + 2] = imageData[i * 3 + 2];
			tempData[i * 4 + 3] = 255;
		}
		imageData = tempData;
	}

	// DirectX Creation
	// Texture description
	D3D11_TEXTURE2D_DESC desc;
	D3D11_SUBRESOURCE_DATA initialData;

	desc.Width = imageTexelSizeX;
	desc.Height = imageTexelSizeY;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	desc.SampleDesc.Count = 1; // MSAA
	desc.SampleDesc.Quality = 0;

	desc.Usage = D3D11_USAGE_IMMUTABLE; // mip-chains : GPU/DEFAULT
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;     // For extension feature


	initialData.pSysMem = imageData;
	initialData.SysMemPitch = imageTexelSizeX * 4;
	initialData.SysMemSlicePitch = 0;

	ID3D11Texture2D* texHandle = nullptr;
	m_device->CreateTexture2D(&desc, &initialData, &texHandle);


	Texture* newTex = new Texture(this, texHandle, imageFilePath);
	m_loadedTextures.push_back(newTex);

	return newTex;
}
            

RenderContext

RenderContext 是图形系统的核心,负责创建图形处理接口与图形管线,同时管理纹理、Shader 等全部图形资源。


Bullet

这是项目 Incursion中的子弹代码。它继承自 Entity,并包含一些独立行为。

#include "Game/Bullet.hpp"
#include "Game/GameCommon.hpp"
#include "Engine/Math/AABB2.hpp"
#include "Engine/Math/MathUtils.hpp"
#include "Game/Explosion.hpp"

Bullet::Bullet(Map* map, const Vec2& pos, EntityType type, EntityFaction faction)
	: Entity(map, pos, type, faction)
{
	m_cosmeticRadius = BULLET_COSMETIC_RADIUS;
	m_physicsRadius = BULLET_PHYSICS_RADIUS;
	m_texture = g_theRenderer->CreateOrGetTextureFromFile("Data/Images/Bullet.png");

	/************************************************************************/
	/* Physics settings                                                     */
	/************************************************************************/
	m_isPushedByEntities = false;
	m_pushesEntities = false;
	m_isPushedByWall = false;
	m_isHitByBullets = false;
}

void Bullet::SetInitOrientationDegrees(float orientationDegrees)
{
	m_orientationDegrees = orientationDegrees;
	m_velocity = Vec2::MakeFromPolarDegrees(orientationDegrees, BULLET_SPEED);
}

void Bullet::Update(float deltaSeconds)
{

	Entity::Update(deltaSeconds);
}

void Bullet::Render() const
{
	std::vector vertexes;
	AppendVertsForAABB2D(vertexes, GetAABB2D(), Rgba8::white);
	TransformVertexArray(vertexes, 1.f, m_orientationDegrees, m_position);

	g_theRenderer->BindTexture(m_texture);
	g_theRenderer->DrawVertexArray(vertexes);
}

void Bullet::Die()
{
	Entity::Die();
	m_isGarbage = true;

	PlaySoundOnce("Data/Audio/BulletHit.wav");
	Explosion* newExplosion = (Explosion*)m_map->SpawnNewEntity(ENTITY_TYPE_EXPLOSION, FACTION_NEUTRAL, m_position);
	newExplosion->SetSize(.2f);
	newExplosion->SetDuration(.5f);
}

AABB2 Bullet::GetAABB2D() const
{
	float scale = .1f;
	return AABB2::MakeFromDimentions(scale, scale);
}

void Bullet::Reflect(Vec2 normal)
{
	Vec2 i = GetProjectedOnto2D(m_velocity, normal);
	Vec2 j = m_velocity - i;

	PlaySoundOnce("Data/Audio/BulletHit.wav");
	m_velocity = (-i) + j;
	m_orientationDegrees = m_velocity.GetOrientationDegrees();
}

            

项目复盘


做得好的地方

  • 学会了如何从零开发游戏引擎
  • 在制作游戏的过程中遇到并解决了数百个引擎问题

需要改进的地方

  • 保持代码库整洁清晰非常困难。尤其在将图形引擎从 OpenGL 移植到 DirectX 时,代码变得更加复杂,也更难维护。
  • 开发某些功能时,我有时会直接硬编码;后来想在其基础上扩展时,就不得不进行大量返工。

我的收获

  • 与能力出色的开发者一起协作非常有趣
  • 引擎与游戏相互影响:游戏基于引擎开发,而引擎功能又源于游戏的实际需求