2019 年 8 月—至今
Starship 是较早基于该引擎开发的游戏,是经典游戏 Asteroids 的改编版本。开发当时,引擎尚不支持文字与纹理渲染,因此我用简单三角形绘制了所有内容。
游戏中需要击毁所有敌方飞船与小行星才能获胜。
游戏包含 5 个关卡、演示画面和结束画面。
操作方式
调试操作
游戏操作
Incursion 是另一款经典游戏 Battle City。
游戏中需要在尽量存活的前提下抵达地图出口才能获胜。玩家可以发射子弹击败敌方坦克与炮塔。
游戏包含 3 个关卡、演示画面、暂停画面和结束画面。
操作方式
调试操作
游戏操作
#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 是图形系统的核心,负责创建图形处理接口与图形管线,同时管理纹理、Shader 等全部图形资源。
这是项目 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();
}