Personal Game Engine

August 2019 - Present


Project Information


Starship

Starship is an early game developed on this engine, which was a revised version of the old game Asteroids . While developing this game, my engine isn't capable of rendering text and textures, but I managed to render everything with simple triangles.

In this game, you need to shoot off all enemy ships and asteroids to win the game.

The game has 5 levels, an attract screen, and a finish screen.


Controls

For Debug

  • Press P to Pause
  • Press T to Enter slow mode
  • Press O to spawn new asteroids
  • Press F1 to trigger the debug mode
  • Press F8 to hard-restart the game

For gameplay

  • Press Up to accelerate the ship
  • Press Left/Right to rotate the ship
  • Press Space to fire bullets
  • Press N to respawn
  • Press Esc to go back to Attract mode

Incursion

Incursion is another replica of the old game Battle City.

In this game, you need to get to the exit of the map and try your best not to die to win the game. You can shoot bullets to defeat the enemy tanks and towers.

The game has 3 levels, an attract screen, a pause screen and a finish screen.


Controls

For Debug

  • Press P to Pause
  • Press T to Enter slow mode (1/10x speed)
  • Press Y to Enter speedup mode (4x speed)
  • Press F1 to toogle the debug mode (display radius/velocity)
  • Press F3 to toogle "no-clip" mode, enabling the tank to move through solid tiles
  • Press F4 to toogle the debug camera, which shows the entire current Map
  • Press F8 to hard-restart the game

For gameplay

  • Start Menu - Press Enter to Start
  • Start Menu - Press Esc to quit
  • Play - Press Arrow Keys to move the tank
  • Play - Press Space Key to shoot
  • Play - Press P/Esc to pause game
  • Pause - Press P to resume game
  • Pause - Press Esc to go back to attract mode
  • Fail/Win - Press Esc to go back to attract mode
  • Fail/Win - Press P (If there're remaining lives) to resume game

#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

The RenderContext is the core of the graphics system, it handles the creation of the graphics handler, as well as the graphics pipeline. It also handles all graphics resources such as Textures and Shaders.


Bullet

This is the bullet code from project Incursion. It's derived from Entity and has some individual behaviours.

#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();
}

            

Post Mortem


What Went Well

  • Learned how to create a game engine from scratch
  • Faced and solved hundreds of problems of the engine while creating the games

What Went Wrong

  • Maintaining the codebase clean and pretty is really hard. Especially when porting the graphics engine from OpenGL to DirectX, the codes are becoming more complicated and hard to maintain.
  • Sometimes when developing a feature I just hard coded the functions, but when I want to add more features based on that, I need to rework a lot.

What I learned

  • Developing with a team of strong developers is really fun
  • Engine and Games are related on each other, games are developed based on engines, and engine features are developed from games