712 lines
27 KiB
C++
712 lines
27 KiB
C++
#include "pch.h"
|
|
#include "Renderer.h"
|
|
#include "Mesh.h"
|
|
#include "Utils.h"
|
|
#include "Texture.h"
|
|
#include "Effects/Effect.h"
|
|
#include "Effects/FireEffect.h"
|
|
#include "HitTest.h"
|
|
#include "Scenes/MainScene.h"
|
|
#include "Scenes/DioramaScene.h"
|
|
#include "Scenes/InstancedScene.h"
|
|
#include "Scenes/PlanetScene.h"
|
|
|
|
|
|
namespace dae {
|
|
|
|
Renderer::Renderer(SDL_Window *pWindow) :
|
|
m_pWindow(pWindow) {
|
|
//Initialize
|
|
SDL_GetWindowSize(pWindow, &m_Width, &m_Height);
|
|
|
|
//Initialize DirectX pipeline
|
|
const HRESULT result = InitializeDirectX();
|
|
if (result == S_OK) {
|
|
m_IsInitialized = true;
|
|
std::cout << GREEN << "DirectX is initialized and ready!" << RESET << std::endl;
|
|
} else {
|
|
std::cout << RED << "DirectX initialization failed!" << RESET << std::endl;
|
|
}
|
|
|
|
//Best camera positions (Sorry for the magic numbers)
|
|
m_SceneCameraPositions[SceneNames::Main] = {Vector3{0.f, 0.f, 0.f}, Vector3{0.f, 0.f, 0.f}};
|
|
m_SceneCameraPositions[SceneNames::Diorama] = {Vector3{48.88f, 23.0f, -3.3f}, Vector3{-0.23f, 4.79f, 0}};
|
|
m_SceneCameraPositions[SceneNames::Instanced] = {Vector3{11.8f, 12.4f, 12.9f}, Vector3{-0.30f, 0.76f, 0.f}};
|
|
m_SceneCameraPositions[SceneNames::Planet] = {Vector3{-18.5f, 10.5f, -15.7f}, Vector3{-0.46f, 0.86f, 0}};
|
|
|
|
InitializeSDLRasterizer();
|
|
|
|
m_pScene = std::make_unique<MainScene>();
|
|
m_pScene->Initialize(m_DevicePtr, m_DeviceContextPtr, nullptr);
|
|
|
|
if (!m_pScene->GetMeshes().empty()) {
|
|
m_pFireMesh = m_pScene->GetMeshes().back().get();
|
|
}
|
|
|
|
const float aspectRatio = static_cast<float>(m_Width) / static_cast<float>(m_Height);
|
|
m_Camera = Camera({.0f, .0f, .0f}, 45.f);
|
|
m_Camera.Initialize(45.f, {.0f, .0f, .0f}, aspectRatio);
|
|
}
|
|
|
|
Renderer::~Renderer() {
|
|
m_RenderTargetViewPtr->Release();
|
|
m_RenderTargetBufferPtr->Release();
|
|
|
|
m_DepthStencilViewPtr->Release();
|
|
m_DepthStencilBufferPtr->Release();
|
|
|
|
m_SwapChainPtr->Release();
|
|
|
|
if (m_DeviceContextPtr) {
|
|
m_DeviceContextPtr->ClearState();
|
|
m_DeviceContextPtr->Flush();
|
|
m_DeviceContextPtr->Release();
|
|
}
|
|
|
|
m_DevicePtr->Release();
|
|
|
|
m_pScene->Cleanup();
|
|
//SDL
|
|
|
|
SDL_FreeSurface(m_pBackBuffer);
|
|
SDL_FreeSurface(m_pFrontBuffer);
|
|
|
|
delete[] m_pDepthBufferPixels;
|
|
|
|
}
|
|
|
|
void Renderer::Update(const Timer *pTimer) {
|
|
m_Camera.Update(pTimer);
|
|
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
mesh->SetCameraPos(m_Camera.GetPosition());
|
|
}
|
|
if (m_Rotating) {
|
|
float rotationThisFrame = pTimer->GetElapsed() * (45.0f * TO_RADIANS); // 45 degrees/sec to radians/sec
|
|
|
|
m_currentRotation += rotationThisFrame;
|
|
|
|
Matrix rotationMatrix = Matrix::CreateRotationY(rotationThisFrame);
|
|
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
Matrix originalWorldMatrix = mesh->GetWorldMatrix();
|
|
Matrix world = rotationMatrix * originalWorldMatrix;
|
|
|
|
mesh->SetWorldMatrix(world);
|
|
}
|
|
}
|
|
|
|
m_pScene->Update();
|
|
}
|
|
|
|
void Renderer::Render() {
|
|
if (!m_IsInitialized)
|
|
return;
|
|
if (m_backendType == Backendtype::DirectX) {
|
|
this->RenderDirectX();
|
|
} else {
|
|
this->RenderSDL();
|
|
}
|
|
|
|
}
|
|
|
|
void Renderer::RenderDirectX() const {
|
|
//Clear back buffer
|
|
ColorRGB ChosenClearColor = m_UseUniformClearColor ? m_UniformClearColor : DirectXClearColor;
|
|
const float clearColor[] = {static_cast<float>(ChosenClearColor.r) / 255.f, static_cast<float>(ChosenClearColor.g) / 255.f,
|
|
static_cast<float>(ChosenClearColor.b) / 255.f, 1.f};
|
|
m_DeviceContextPtr->ClearRenderTargetView(m_RenderTargetViewPtr, clearColor);
|
|
|
|
m_DeviceContextPtr->ClearDepthStencilView(m_DepthStencilViewPtr, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
|
|
|
Matrix viewProjMatrix = m_Camera.GetViewProjectionMatrix();
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
if (mesh->GetShouldRender()) {
|
|
Matrix modelMatrix = mesh->GetWorldMatrix();
|
|
Matrix worldViewProjMatrix = modelMatrix * viewProjMatrix;
|
|
|
|
mesh->Render(m_DeviceContextPtr, worldViewProjMatrix);
|
|
}
|
|
}
|
|
|
|
m_pScene->Render(m_DeviceContextPtr, m_RenderTargetViewPtr, m_DepthStencilViewPtr, m_Camera);
|
|
|
|
//Present
|
|
m_SwapChainPtr->Present(0, 0);
|
|
}
|
|
|
|
void Renderer::RenderSDL() {
|
|
ColorRGB ChosenClearColor = m_UseUniformClearColor ? m_UniformClearColor : m_ClearColorSoftware;
|
|
SDL_FillRect(m_pBackBuffer, nullptr, SDL_MapRGB(m_pBackBuffer->format, ChosenClearColor.r, ChosenClearColor.g, ChosenClearColor.b));
|
|
|
|
SDL_LockSurface(m_pBackBuffer);
|
|
std::fill_n(m_pDepthBufferPixels, m_Width * m_Height, std::numeric_limits<float>::max());
|
|
|
|
ColorRGB finalColor{};
|
|
constexpr int numVerticies = 3;
|
|
|
|
m_VerticiesScreenSpace.clear();
|
|
|
|
|
|
for (auto& currentMesh: m_pScene->GetMeshes()) {
|
|
|
|
if (!currentMesh->GetShouldRender()) {
|
|
continue;
|
|
}
|
|
|
|
const Matrix worldViewProjectionMatrix{currentMesh->GetWorldMatrix() * m_Camera.GetViewProjectionMatrix()};
|
|
VertexTransformationFunction(worldViewProjectionMatrix, currentMesh.get(), currentMesh->GetVertices(), m_VerticiesScreenSpace);
|
|
|
|
int numTriangles{};
|
|
|
|
switch (currentMesh->GetPrimitiveTopology()) {
|
|
case PrimitiveTopology::TriangleList:
|
|
numTriangles = static_cast<int>(currentMesh->GetIndices().size()) / numVerticies;
|
|
break;
|
|
case PrimitiveTopology::TriangleStrip:
|
|
numTriangles = static_cast<int>(currentMesh->GetIndices().size()) - 2;
|
|
break;
|
|
}
|
|
|
|
|
|
for (int triangleIndex{}; triangleIndex < numTriangles; ++triangleIndex) {
|
|
|
|
VertexOut vertex0, vertex1, vertex2;
|
|
|
|
switch (currentMesh->GetPrimitiveTopology()) {
|
|
case PrimitiveTopology::TriangleList:
|
|
vertex0 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex * numVerticies + 0]];
|
|
vertex1 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex * numVerticies + 1]];
|
|
vertex2 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex * numVerticies + 2]];
|
|
break;
|
|
case PrimitiveTopology::TriangleStrip:
|
|
vertex0 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex + 0]];
|
|
vertex1 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex + 1]];
|
|
vertex2 = m_VerticiesScreenSpace[currentMesh->GetIndices()[triangleIndex + 2]];
|
|
|
|
if (triangleIndex % 2 == 1) {
|
|
std::swap(vertex1, vertex2);
|
|
}
|
|
|
|
if (vertex0.position == vertex1.position ||
|
|
vertex0.position == vertex2.position ||
|
|
vertex1.position == vertex2.position) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (!vertex0.valid and !vertex1.valid and !vertex2.valid) {
|
|
continue;
|
|
}
|
|
|
|
|
|
const float minX{std::min(vertex0.position.x, std::min(vertex1.position.x, vertex2.position.x))};
|
|
const float minY{std::min(vertex0.position.y, std::min(vertex1.position.y, vertex2.position.y))};
|
|
|
|
const float maxX{std::max(vertex0.position.x, std::max(vertex1.position.x, vertex2.position.x))};
|
|
const float maxY{std::max(vertex0.position.y, std::max(vertex1.position.y, vertex2.position.y))};
|
|
|
|
|
|
const Vector3 side1{vertex1.position - vertex0.position};
|
|
const Vector3 side2{vertex2.position - vertex0.position};
|
|
|
|
const float totalArea{Vector3::Cross(side1, side2).z};
|
|
|
|
const int startX = std::max(0, static_cast<int>(minX)) - 1;
|
|
const int endX = std::min(m_Width - 1, static_cast<int>(maxX)) + 1;
|
|
|
|
const int startY = std::max(0, static_cast<int>(minY)) - 1;
|
|
const int endY = std::min(m_Height - 1, static_cast<int>(maxY)) + 1;
|
|
|
|
for (int px{startX}; px < endX; ++px) {
|
|
for (int py{startY}; py < endY; ++py) {
|
|
|
|
if (m_isHitbox) {
|
|
//Hitboxes
|
|
m_pBackBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
|
|
static_cast<uint8_t>(255),
|
|
static_cast<uint8_t>(255),
|
|
static_cast<uint8_t>(255));
|
|
continue;
|
|
}
|
|
|
|
if (px < 0 || px >= m_Width || py < 0 || py >= m_Height) {
|
|
//TEMP fix for out of bounds
|
|
//This is to remove triangles having an incorrect edge on their bounding box
|
|
continue;
|
|
}
|
|
|
|
Vector3 P{static_cast<float>(px) + 0.5f, static_cast<float>(py) + 0.5f, 1.f};
|
|
|
|
auto sample{TriangleHitTest(P, vertex0, vertex1, vertex2)};
|
|
|
|
if (!sample.has_value()) {
|
|
continue;
|
|
}
|
|
|
|
const Vector3 fragPos{static_cast<float>(px) + 0.5f, static_cast<float>(py) + 0.5f, 1.f};
|
|
|
|
int depthBufferIndex{px + (py * m_Width)};
|
|
|
|
float min{.985f};
|
|
float max{1.f};
|
|
float depthBuffer{(sample.value().depth - min) * (max - min)};
|
|
float currentDepth = sample.value().depth;
|
|
|
|
if (m_pDepthBufferPixels[depthBufferIndex] > currentDepth) {
|
|
m_pDepthBufferPixels[depthBufferIndex] = currentDepth;
|
|
|
|
if (m_isDepthBuffer) {
|
|
finalColor = ColorRGB{depthBuffer, depthBuffer, depthBuffer};
|
|
} else {
|
|
finalColor = ShadePixel(sample.value());
|
|
}
|
|
|
|
finalColor.MaxToOne();
|
|
|
|
m_pBackBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
|
|
static_cast<uint8_t>(finalColor.r * 255),
|
|
static_cast<uint8_t>(finalColor.g * 255),
|
|
static_cast<uint8_t>(finalColor.b * 255));
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_UnlockSurface(m_pBackBuffer);
|
|
SDL_BlitSurface(m_pBackBuffer, nullptr, m_pFrontBuffer, nullptr);
|
|
SDL_UpdateWindowSurface(m_pWindow);
|
|
}
|
|
|
|
HRESULT Renderer::InitializeDirectX() {
|
|
//1. Create device & deviceContext
|
|
//=====
|
|
D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_1;
|
|
uint32_t createDeviceFlags = 0;
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
#endif
|
|
|
|
HRESULT result = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, &featureLevel,
|
|
1, D3D11_SDK_VERSION, &m_DevicePtr, nullptr, &m_DeviceContextPtr);
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//Create DXGI factory
|
|
IDXGIFactory1 *DxgiFactoryPtr{};
|
|
result = CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void **>(&DxgiFactoryPtr));
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//2. Create swap chain
|
|
//=====
|
|
DXGI_SWAP_CHAIN_DESC swapChainDesc{};
|
|
swapChainDesc.BufferDesc.Width = m_Width;
|
|
swapChainDesc.BufferDesc.Height = m_Height;
|
|
swapChainDesc.BufferDesc.RefreshRate.Numerator = 1;
|
|
swapChainDesc.BufferDesc.RefreshRate.Denominator = 60;
|
|
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
swapChainDesc.SampleDesc.Count = 1;
|
|
swapChainDesc.SampleDesc.Quality = 0;
|
|
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapChainDesc.BufferCount = 1;
|
|
swapChainDesc.Windowed = true;
|
|
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
swapChainDesc.Flags = 0;
|
|
|
|
//Get the handle (HWND) from the SDL back buffer
|
|
SDL_SysWMinfo sysWMInfo{};
|
|
SDL_GetVersion(&sysWMInfo.version);
|
|
SDL_GetWindowWMInfo(m_pWindow, &sysWMInfo);
|
|
swapChainDesc.OutputWindow = sysWMInfo.info.win.window;
|
|
|
|
//Create SwapChain
|
|
result = DxgiFactoryPtr->CreateSwapChain(m_DevicePtr, &swapChainDesc, &m_SwapChainPtr);
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//3. Create depthStencil (DS) & DepthStencilView (DSV)
|
|
//=====
|
|
//Resource
|
|
D3D11_TEXTURE2D_DESC depthStencilDesc{};
|
|
depthStencilDesc.Width = m_Width;
|
|
depthStencilDesc.Height = m_Height;
|
|
depthStencilDesc.MipLevels = 1;
|
|
depthStencilDesc.ArraySize = 1;
|
|
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
depthStencilDesc.SampleDesc.Count = 1;
|
|
depthStencilDesc.SampleDesc.Quality = 0;
|
|
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
|
depthStencilDesc.CPUAccessFlags = 0;
|
|
depthStencilDesc.MiscFlags = 0;
|
|
|
|
//View
|
|
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc{};
|
|
depthStencilViewDesc.Format = depthStencilDesc.Format;
|
|
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
|
|
depthStencilViewDesc.Texture2D.MipSlice = 0;
|
|
|
|
result = m_DevicePtr->CreateTexture2D(&depthStencilDesc, nullptr, &m_DepthStencilBufferPtr);
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
result = m_DevicePtr->CreateDepthStencilView(m_DepthStencilBufferPtr, &depthStencilViewDesc, &m_DepthStencilViewPtr);
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//.4 Create RenderTarget (RT) & RenderTargetView (RTV)
|
|
|
|
//Resource
|
|
result = m_SwapChainPtr->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_RenderTargetBufferPtr));
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//View
|
|
result = m_DevicePtr->CreateRenderTargetView(m_RenderTargetBufferPtr, nullptr, &m_RenderTargetViewPtr);
|
|
if (FAILED(result))
|
|
return result;
|
|
|
|
//5. Bind RTV & DSV to output Merger stage
|
|
//=====
|
|
m_DeviceContextPtr->OMSetRenderTargets(1, &m_RenderTargetViewPtr, m_DepthStencilViewPtr);
|
|
|
|
//6. Set Viewport
|
|
//=====
|
|
D3D11_VIEWPORT viewport{};
|
|
viewport.Width = static_cast<float>(m_Width);
|
|
viewport.Height = static_cast<float>(m_Height);
|
|
viewport.TopLeftX = 0.f;
|
|
viewport.TopLeftY = 0.f;
|
|
viewport.MinDepth = 0.f;
|
|
viewport.MaxDepth = 1.f;
|
|
m_DeviceContextPtr->RSSetViewports(1, &viewport);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void Renderer::InitializeSDLRasterizer() {
|
|
m_pFrontBuffer = SDL_GetWindowSurface(m_pWindow);
|
|
m_pBackBuffer = SDL_CreateRGBSurface(0, m_Width, m_Height, 32, 0, 0, 0, 0);
|
|
m_pBackBufferPixels = (uint32_t *) m_pBackBuffer->pixels;
|
|
|
|
m_pDepthBufferPixels = new float[m_Width * m_Height];
|
|
}
|
|
|
|
|
|
void Renderer::VertexTransformationFunction(const Matrix &WorldViewProjectionMatrix, Mesh *mesh,
|
|
std::vector<VertexIn> &vertices_in, std::vector<VertexOut> &vertices_out) const {
|
|
for (const VertexIn &vert: vertices_in) {
|
|
VertexOut vertex_out{};
|
|
|
|
Vector4 vertPos{WorldViewProjectionMatrix.TransformPoint({vert.position, 1})};
|
|
|
|
const Vector3 normal{mesh->GetWorldMatrix().TransformVector(vert.normal).Normalized()};
|
|
const Vector3 tangent{mesh->GetWorldMatrix().TransformVector(vert.tangent).Normalized()};
|
|
|
|
|
|
vertPos.x /= vertPos.w;
|
|
vertPos.y /= vertPos.w;
|
|
vertPos.z /= vertPos.w;
|
|
|
|
bool isValid{true};
|
|
|
|
//Check if the vertex is inside the screen
|
|
if (vertPos.x < -1.f || vertPos.x > 1.f ||
|
|
vertPos.y < -1.f || vertPos.y > 1.f ||
|
|
vertPos.z < 0.f || vertPos.z > 1.f)
|
|
isValid = false;
|
|
|
|
vertPos.x = ((vertPos.x + 1.f) / 2.f) * static_cast<float>(m_Width);
|
|
vertPos.y = ((1.f - vertPos.y) / 2.f) * static_cast<float>(m_Height);
|
|
|
|
vertex_out.position = vertPos;
|
|
vertex_out.uv = vert.uv;
|
|
vertex_out.normal = normal;
|
|
vertex_out.tangent = tangent;
|
|
vertex_out.mesh = mesh;
|
|
vertex_out.valid = isValid;
|
|
vertex_out.viewDir = (mesh->GetWorldMatrix().TransformPoint(vertex_out.position)
|
|
- m_Camera.GetPosition().ToPoint4()).Normalized().GetXYZ();
|
|
|
|
vertices_out.push_back(vertex_out);
|
|
}
|
|
|
|
}
|
|
|
|
void Renderer::ToggleDepthBuffer() {
|
|
m_isDepthBuffer = !m_isDepthBuffer;
|
|
|
|
std::string mode = m_isDepthBuffer ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[SOFTWARE]" << BLUE << " Depth Buffer " << mode << RESET << std::endl;
|
|
}
|
|
|
|
ColorRGB Renderer::ShadePixel(const Sample &sample) {
|
|
|
|
Material *currentMaterial = sample.mesh->GetMaterial();
|
|
|
|
if (currentMaterial->normalTexturePtr == nullptr && currentMaterial->specularTexturePtr == nullptr &&
|
|
currentMaterial->glossTexturePtr == nullptr) {
|
|
return currentMaterial->diffuseTexturePtr->Sample(sample.uv);
|
|
}
|
|
|
|
Vector3 lightDirection = {.577f, -.577f, .577f};
|
|
Vector3 normal = sample.normal.Normalized();
|
|
constexpr float lightIntensity{7.f};
|
|
|
|
ColorRGB color{1, 1, 1};
|
|
constexpr ColorRGB ambient{.03f, .03f, .03f};
|
|
|
|
if (m_useNormals) {
|
|
|
|
const Vector3 biNormal{Vector3::Cross(normal, sample.tangent)};
|
|
|
|
const Matrix tangentToWorld{
|
|
sample.tangent,
|
|
biNormal,
|
|
normal,
|
|
{0.f, 0.f, 0.f}
|
|
};
|
|
|
|
const ColorRGB normalSample{sample.mesh->GetMaterial()->normalTexturePtr->Sample(sample.uv)};
|
|
|
|
Vector4 normalMapSample{
|
|
2.f * normalSample.r - 1.f,
|
|
2.f * normalSample.g - 1.f,
|
|
2.f * normalSample.b - 1.f,
|
|
0.f
|
|
};
|
|
|
|
normal = tangentToWorld.TransformVector(normalMapSample).Normalized();
|
|
}
|
|
|
|
const ColorRGB diffuseSample{currentMaterial->diffuseTexturePtr->Sample(sample.uv)};
|
|
double invPi = 1.0f / PI;
|
|
const ColorRGB lambert{diffuseSample * lightIntensity / PI};
|
|
|
|
//TODO: ask why deviding by PI causses Segmentation fault
|
|
// const ColorRGB lambert{ diffuseSample * lightIntensity / PI };
|
|
|
|
|
|
float specularReflectance{1.f};
|
|
float shininess{25.f};
|
|
|
|
specularReflectance *= currentMaterial->specularTexturePtr->Sample(sample.uv).r;
|
|
shininess *= currentMaterial->glossTexturePtr->Sample(sample.uv).r;
|
|
|
|
const float cosAngle = Vector3::Dot(normal, -lightDirection);
|
|
|
|
const Vector3 reflect = Vector3::Reflect(lightDirection, normal);
|
|
float cosSpecular = Vector3::Dot(reflect, sample.viewDirection);
|
|
cosSpecular = std::max(cosSpecular, 0.f);
|
|
|
|
const ColorRGB specular = specularReflectance * powf(cosSpecular, shininess) * colors::White;
|
|
|
|
if (cosAngle < 0) {
|
|
return ambient;
|
|
}
|
|
|
|
switch (m_ShadeMode) {
|
|
case ShadeMode::ObservedArea:
|
|
color = ColorRGB{cosAngle, cosAngle, cosAngle};
|
|
break;
|
|
case ShadeMode::Diffuse:
|
|
color = lambert;
|
|
break;
|
|
case ShadeMode::Specular:
|
|
color = specular;
|
|
break;
|
|
case ShadeMode::Combined:
|
|
color = lambert + specular + ambient;
|
|
color *= ColorRGB{cosAngle, cosAngle, cosAngle};
|
|
break;
|
|
}
|
|
|
|
|
|
return color;
|
|
}
|
|
|
|
void Renderer::CycleCullMode() {
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
mesh->CycleCullMode();
|
|
}
|
|
|
|
std::string mode;
|
|
switch (m_CullMode) {
|
|
case CullMode::None:
|
|
m_CullMode = CullMode::Front;
|
|
mode = "Front";
|
|
break;
|
|
case CullMode::Front:
|
|
m_CullMode = CullMode::Back;
|
|
mode = "Back";
|
|
break;
|
|
case CullMode::Back:
|
|
m_CullMode = CullMode::None;
|
|
mode = "None";
|
|
break;
|
|
}
|
|
|
|
std::cout << MAGENTA << "[HARDWARE]" << BLUE << " Cull Mode = " << mode << RESET << std::endl;
|
|
}
|
|
|
|
void Renderer::ToggleBoundingBox() {
|
|
m_isHitbox = !m_isHitbox;
|
|
|
|
std::string mode = m_isHitbox ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[SOFTWARE]" << BLUE << " Hitbox " << mode << RESET << std::endl;
|
|
|
|
}
|
|
|
|
void Renderer::CycleRenderingMode() {
|
|
|
|
std::string mode;
|
|
|
|
switch (m_ShadeMode) {
|
|
case ShadeMode::ObservedArea:
|
|
m_ShadeMode = ShadeMode::Diffuse;
|
|
mode = "Diffuse";
|
|
break;
|
|
case ShadeMode::Diffuse:
|
|
m_ShadeMode = ShadeMode::Specular;
|
|
mode = "Specular";
|
|
break;
|
|
case ShadeMode::Specular:
|
|
m_ShadeMode = ShadeMode::Combined;
|
|
mode = "Combined";
|
|
break;
|
|
case ShadeMode::Combined:
|
|
m_ShadeMode = ShadeMode::ObservedArea;
|
|
mode = "Observed Area";
|
|
break;
|
|
}
|
|
|
|
std::cout << MAGENTA << "[SOFTWARE]" << BLUE << " Shading Mode = " << mode << RESET << std::endl;
|
|
}
|
|
|
|
|
|
void Renderer::SwitchBackend() {
|
|
if (m_backendType == Backendtype::DirectX) {
|
|
m_backendType = Backendtype::SDL;
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Rasterizer mode = SOFTWARE" << RESET << std::endl;
|
|
} else {
|
|
m_backendType = Backendtype::DirectX;
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Rasterizer mode = HARDWARE" << RESET << std::endl;
|
|
}
|
|
}
|
|
|
|
void Renderer::NextSamplingState() {
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
mesh->NextSamplingState();
|
|
}
|
|
|
|
std::string mode;
|
|
switch (m_TechniqueType) {
|
|
case TechniqueType::Point:
|
|
m_TechniqueType = TechniqueType::Linear;
|
|
mode = "Linear";
|
|
break;
|
|
case TechniqueType::Linear:
|
|
m_TechniqueType = TechniqueType::Anisotropic;
|
|
mode = "Anisotropic";
|
|
break;
|
|
case TechniqueType::Anisotropic:
|
|
m_TechniqueType = TechniqueType::Point;
|
|
mode = "Point";
|
|
break;
|
|
}
|
|
|
|
std::cout << MAGENTA << "[HARDWARE]" << BLUE << " Texture Sampling Mode = " << mode << RESET << std::endl;
|
|
}
|
|
|
|
void Renderer::ToggleNormals() {
|
|
m_useNormals = !m_useNormals;
|
|
for (auto& mesh: m_pScene->GetMeshes()) {
|
|
mesh->ToggleNormals();
|
|
}
|
|
std::string mode = m_useNormals ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[SOFTWARE]" << BLUE << " Normal Map " << mode << RESET << std::endl;
|
|
}
|
|
|
|
void Renderer::ToggleRotation() {
|
|
m_Rotating = !m_Rotating;
|
|
std::string mode = m_Rotating ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Model Rotation " << mode << RESET << std::endl;
|
|
}
|
|
|
|
void Renderer::ToggleUniformClearColor() {
|
|
m_UseUniformClearColor = !m_UseUniformClearColor;
|
|
std::string mode = m_UseUniformClearColor ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Uniform Clear Color " << mode << RESET << std::endl;
|
|
}
|
|
|
|
void Renderer::ToggleFireFX() {
|
|
if (m_pFireMesh != nullptr) {
|
|
m_pFireMesh->SetShouldRender(!m_pFireMesh->GetShouldRender());
|
|
std::string mode = m_pFireMesh->GetShouldRender() ? "ON" : "OFF";
|
|
std::cout << MAGENTA << "[HARDWARE]" << BLUE << " FireFX " << mode << RESET << std::endl;
|
|
}
|
|
}
|
|
|
|
void Renderer::NextScene() {
|
|
m_pScene->Cleanup();
|
|
|
|
//Calculate the next scene
|
|
int index = static_cast<int>(m_CurrentScene);
|
|
index = (index + 1) % static_cast<int>(SceneNames::Count);
|
|
m_CurrentScene = static_cast<SceneNames>(index);
|
|
|
|
switch (m_CurrentScene) {
|
|
case SceneNames::Main:
|
|
m_pScene = std::make_unique<MainScene>();
|
|
m_pFireMesh = nullptr;
|
|
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Scene = Main" << RESET << std::endl;
|
|
std::cout << MAGENTA << "This could take a second" << RESET << std::endl;
|
|
break;
|
|
case SceneNames::Diorama:
|
|
m_pScene = std::make_unique<DioramaScene>();
|
|
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Scene = Diorama" << RESET << std::endl;
|
|
std::cout << MAGENTA << "This could take a second" << RESET << std::endl;
|
|
break;
|
|
case SceneNames::Instanced:
|
|
m_pScene = std::make_unique<InstancedScene>();
|
|
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Scene = Instanced" << RESET << std::endl;
|
|
std::cout << MAGENTA << "This could take a second" << RESET << std::endl;
|
|
break;
|
|
|
|
case SceneNames::Planet:
|
|
m_pScene = std::make_unique<PlanetScene>();
|
|
|
|
std::cout << MAGENTA << "[SHARED]" << BLUE << " Scene = Planet" << RESET << std::endl;
|
|
std::cout << MAGENTA << "This could take a second" << RESET << std::endl;
|
|
break;
|
|
|
|
case SceneNames::Count:
|
|
break;
|
|
}
|
|
|
|
m_Camera.SetPosition(m_SceneCameraPositions[m_CurrentScene].first);
|
|
m_Camera.SetRotation(m_SceneCameraPositions[m_CurrentScene].second);
|
|
|
|
m_pScene->Initialize(m_DevicePtr, m_DeviceContextPtr, &m_Camera);
|
|
|
|
|
|
if (m_CurrentScene == SceneNames::Main) {
|
|
//Kind of sloppy fix but hey :p
|
|
m_pFireMesh = m_pScene->GetMeshes().back().get();
|
|
}
|
|
}
|
|
|
|
|
|
}
|