225 lines
8.8 KiB
C++
225 lines
8.8 KiB
C++
//External includes
|
|
#include <iostream>
|
|
#include "SDL_surface.h"
|
|
|
|
//Project includes
|
|
#include "Renderer.h"
|
|
#include "Maths.h"
|
|
#include "HitTest.h"
|
|
|
|
using namespace dae;
|
|
|
|
Renderer::Renderer(SDL_Window *pWindow) : m_pWindow(pWindow) {
|
|
//Initialize
|
|
SDL_GetWindowSize(pWindow, &m_Width, &m_Height);
|
|
|
|
//Create Buffers
|
|
m_pFrontBuffer = SDL_GetWindowSurface(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];
|
|
|
|
//Initialize Camera
|
|
m_Camera.Initialize(60.f, {.0f, .0f, -10.f});
|
|
|
|
//Initialize Camera
|
|
m_Camera.Initialize(60.f, {.0f, .0f, -10.f});
|
|
m_aspectRatio = static_cast<float>(m_Width) / static_cast<float>(m_Height);
|
|
|
|
m_pTexture = Texture::LoadFromFile("./Resources/uv_grid_2.png");
|
|
}
|
|
|
|
Renderer::~Renderer() {
|
|
delete[] m_pDepthBufferPixels;
|
|
}
|
|
|
|
void Renderer::Update(Timer *pTimer) {
|
|
m_Camera.Update(pTimer);
|
|
}
|
|
|
|
void Renderer::Render() {
|
|
//@START
|
|
//Lock BackBuffer
|
|
//Random color
|
|
SDL_FillRect(m_pBackBuffer, nullptr, m_ClearColor);
|
|
SDL_LockSurface(m_pBackBuffer);
|
|
std::fill_n(m_pDepthBufferPixels, m_Width * m_Height, std::numeric_limits<float>::max());
|
|
|
|
//Clear
|
|
SDL_FillRect(m_pBackBuffer, nullptr, m_ClearColor);
|
|
|
|
|
|
ColorRGB finalColor{};
|
|
constexpr int numVerticies = 3;
|
|
|
|
std::vector<Vertex> verticiesScreenSpace{};
|
|
// VertexTransformationFunction(m_verticiesWorld, verticiesScreenSpace);
|
|
|
|
for (const Mesh ¤tMesh: m_meshesWorldStrip) {
|
|
VertexTransformationFunction(currentMesh.vertices, verticiesScreenSpace);
|
|
|
|
int numTriangles{};
|
|
|
|
switch (currentMesh.primitiveTopology) {
|
|
case PrimitiveTopology::TriangleList:
|
|
numTriangles = static_cast<int>(currentMesh.indices.size()) / numVerticies;
|
|
break;
|
|
case PrimitiveTopology::TriangleStrip:
|
|
numTriangles = static_cast<int>(currentMesh.indices.size()) - 2;
|
|
break;
|
|
}
|
|
|
|
|
|
for (int triangleIndex{}; triangleIndex < numTriangles; ++triangleIndex) {
|
|
|
|
Vertex vertex0, vertex1, vertex2;
|
|
|
|
switch (currentMesh.primitiveTopology) {
|
|
case PrimitiveTopology::TriangleList:
|
|
vertex0 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 0]];
|
|
vertex1 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 1]];
|
|
vertex2 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 2]];
|
|
break;
|
|
case PrimitiveTopology::TriangleStrip:
|
|
vertex0 = verticiesScreenSpace[currentMesh.indices[triangleIndex + 0]];
|
|
vertex1 = verticiesScreenSpace[currentMesh.indices[triangleIndex + 1]];
|
|
vertex2 = verticiesScreenSpace[currentMesh.indices[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;
|
|
}
|
|
|
|
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 (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};
|
|
|
|
const Vector3 cross0{Vector3::Cross(vertex2.position - vertex1.position, P - vertex1.position)};
|
|
if (cross0.z < 0) {
|
|
continue;
|
|
}
|
|
const Vector3 cross1{Vector3::Cross(vertex0.position - vertex2.position, P - vertex2.position)};
|
|
if (cross1.z < 0) {
|
|
continue;
|
|
}
|
|
const Vector3 cross2{Vector3::Cross(vertex1.position - vertex0.position, P - vertex0.position)};
|
|
if (cross2.z < 0) {
|
|
continue;
|
|
}
|
|
|
|
const int depthBufferIndex{px + (py * m_Width)};
|
|
|
|
float totalWeight{cross0.z + cross1.z + cross2.z};
|
|
Vector3 weights{
|
|
cross0.z / totalWeight,
|
|
cross1.z / totalWeight,
|
|
cross2.z / totalWeight,
|
|
};
|
|
|
|
const float currentDepth =
|
|
1 / (weights.x / vertex0.position.z +
|
|
weights.y / vertex1.position.z +
|
|
weights.z / vertex2.position.z);
|
|
|
|
const Vector2 UvCoords =
|
|
vertex0.uv * currentDepth * weights.x / vertex0.position.z +
|
|
vertex1.uv * currentDepth * weights.y / vertex1.position.z +
|
|
vertex2.uv * currentDepth * weights.z / vertex2.position.z;
|
|
|
|
|
|
if (m_pDepthBufferPixels[depthBufferIndex] >= currentDepth) {
|
|
m_pDepthBufferPixels[depthBufferIndex] = currentDepth;
|
|
|
|
//Update Color in Buffer
|
|
finalColor = m_pTexture->Sample(UvCoords);
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int x, y{0};
|
|
//For loop over all the pixels
|
|
for (int px{}; px < 100; ++px) {
|
|
for (int py{}; py < 100; ++py) {
|
|
//Get the pixel position
|
|
x = px;
|
|
y = py;
|
|
|
|
ColorRGB test = m_pTexture->Sample(Vector2{static_cast<float>(px) / 100, static_cast<float>(py) / 100});
|
|
m_pBackBufferPixels[x + (y * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
|
|
static_cast<uint8_t>(test.r * 255),
|
|
static_cast<uint8_t>(test.g * 255),
|
|
static_cast<uint8_t>(test.b * 255));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//RENDER LOGIC
|
|
|
|
SDL_UnlockSurface(m_pBackBuffer);
|
|
SDL_BlitSurface(m_pBackBuffer,
|
|
nullptr, m_pFrontBuffer, nullptr);
|
|
SDL_UpdateWindowSurface(m_pWindow);
|
|
}
|
|
|
|
void Renderer::VertexTransformationFunction(const std::vector<Vertex> &vertices_in,
|
|
std::vector<Vertex> &vertices_out) const {
|
|
for (const Vertex &vert: vertices_in) {
|
|
Vector3 vertPos{m_Camera.invViewMatrix.TransformPoint(vert.position)};
|
|
|
|
vertPos.x = (vertPos.x / vertPos.z) / (m_aspectRatio * m_Camera.fov);
|
|
vertPos.y = (vertPos.y / vertPos.z) / m_Camera.fov;
|
|
|
|
vertPos.x = (vertPos.x + 1) / 2 * static_cast<float>(m_Width);
|
|
vertPos.y = (1 - vertPos.y) / 2 * static_cast<float>(m_Height);
|
|
vertices_out.push_back(Vertex{vertPos, vert.color, vert.uv});
|
|
}
|
|
}
|
|
|
|
bool Renderer::SaveBufferToImage() const {
|
|
return SDL_SaveBMP(m_pBackBuffer, "Rasterizer_ColorBuffer.bmp");
|
|
}
|