ProtoTank/Engine/Graphics/3DRenderer.cpp

119 lines
5.6 KiB
C++

#include "3DRenderer.hpp"
#include "../World/DbgCube.hpp"
#include "../World/Tank.hpp"
// Rendering pipeline:
// model matrix (Object SRT) -> view matrix (camera matrix inverted) -> proj matrix -> clipping -> perspective divide -> viewport transformation -> Rasterizer (draw pixels inside projected triangles on 2D screen)
// object coordinate -> world coordinate -> camera coordinate -> clip/screen coordinate
//
// Rasterizer inputs elements:
// - texture-buffer (2D array of pixels color value)
// - z-buffer (2D array of float representing the nearest pixel's depth, all pixels beyond are ignored)
// - projected vertices-buffer on screen (using vertices-buffer and projection function)
//
// Refs:
// * https://en.wikipedia.org/wiki/3D_projection
// * https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/overview-rasterization-algorithm.html
Graphic3DRenderer::Graphic3DRenderer() {
if (mMainCamera == nullptr) {
mMainCamera = std::make_unique<Camera>();
mMainCamera->SetPosition(0.0f, 3.0f, -20.0f);
mMainCamera->SetFrustrum(90.0f, 1280.f/324.f, 1.0f, 100.f);
mMainCamera->UpdateCamView();
}
// Fill world object list to render
mRenderList.clear();
mRenderList.push_back(std::make_shared<ObjectDbgCube>());
mRenderList.back()->SetPosition(0.f, 0.f, 50.f);
mRenderList.back()->SetScale(10.0f);
mRenderList.push_back(std::make_shared<ObjectDbgCube>());
mRenderList.back()->SetPosition(24.f, 5.f, 12.f);
mRenderList.back()->SetScale(10.0f);
mRenderList.push_back(std::make_shared<ObjectDbgCube>());
mRenderList.back()->SetPosition(-31.f, 16.f, 24.f);
mRenderList.back()->SetScale(10.0f);
mRenderList.push_back(std::make_shared<Tank>());
mRenderList.back()->SetPosition(0.f, 0.f, 0.f);
mRenderList.back()->SetScale(25.0f);
}
Graphic3DRenderer::~Graphic3DRenderer() {}
void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
sf::BlendMode sBM = sf::BlendNone;
sf::RenderStates sRS(sBM);
// Hardcoded debug movement, TODO: remove it
static float thetaAngle = 0.31f;
thetaAngle = thetaAngle >= 6.283185f ? -6.283185f : thetaAngle + 0.004f;
static float thetaAngle2 = 2.12f;
thetaAngle2 = thetaAngle2 >= 6.283185f ? -6.283185f : thetaAngle2 + 0.005f;
static float thetaAngle3 = -4.78f;
thetaAngle3 = thetaAngle3 >= 6.283185f ? -6.283185f : thetaAngle3 + 0.008f;
mRenderList[0]->SetRotation(thetaAngle, 0.f, thetaAngle * 0.5f);
mRenderList[1]->SetRotation(thetaAngle2, 0.f, thetaAngle2 * 0.5f);
mRenderList[2]->SetRotation(thetaAngle3, 0.f, thetaAngle3 * 0.5f);
mRenderList[3]->SetRotation(0.f, thetaAngle, 0.f);
M3D_MATRIX viewProjMat = mMainCamera->GetView() * mMainCamera->GetProj();
sf::Vertex v_tri[4];
uint32_t totVerticesCnt = 0, processedVerticesCnt = 0;
for (auto obj : mRenderList)
totVerticesCnt += obj->GetObjectMesh().GetVerticesCount();
// Do the vertices projection and perspective divide
M3D_F3 projVertices[totVerticesCnt];
for (auto obj : mRenderList) {
auto& oMesh = obj->GetObjectMesh();
M3D_V3TransformPersDiv(
projVertices + processedVerticesCnt, sizeof(M3D_F3),
(M3D_F3*)oMesh.vertices.data(), sizeof(Vertex),
oMesh.vertices.size(),
obj->GetTransform() * viewProjMat
);
//TODO: Fill a z-depth buffer...
for (auto& objPt : obj->GetObjectMesh().parts) {
auto indicePtr = (uint32_t*)objPt.indices.data();
for (uint32_t i = 0; i < objPt.GetIndicesCount(); i += 3) {
// Misscontructed indices tree failsafe
if (i+2 > objPt.GetIndicesCount())
break;
//TODO: Proceed with z-test: if z is lesser than previous z, draw the pixel and update z
// Simple clipping
//TODO: implement complete Cohen-Sutherland algo or similar
if (((projVertices + processedVerticesCnt)[indicePtr[i]]).z > 0 &&
((projVertices + processedVerticesCnt)[indicePtr[i+1]]).z > 0 &&
((projVertices + processedVerticesCnt)[indicePtr[i+2]]).z > 0) {
M3D_VECTOR V1 = M3D_V4LoadF3(&(projVertices + processedVerticesCnt)[indicePtr[i]]);
M3D_VECTOR V2 = M3D_V4LoadF3(&(projVertices + processedVerticesCnt)[indicePtr[i+1]]);
M3D_VECTOR V3 = M3D_V4LoadF3(&(projVertices + processedVerticesCnt)[indicePtr[i+2]]);
V1 = M3D_V3TransformNDCToViewport(V1, 0.f, 0.f, 1280.f, 324.f, 1.f, 100.f);
V2 = M3D_V3TransformNDCToViewport(V2, 0.f, 0.f, 1280.f, 324.f, 1.f, 100.f);
V3 = M3D_V3TransformNDCToViewport(V3, 0.f, 0.f, 1280.f, 324.f, 1.f, 100.f);
v_tri[0].position = sf::Vector2f(M3D_V4GetX(V1), M3D_V4GetY(V1));
v_tri[0].color = oMesh.vertices[indicePtr[i]].color;
v_tri[3] = v_tri[0];
v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2));
v_tri[1].color = oMesh.vertices[indicePtr[i+1]].color;
v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3));
v_tri[2].color = oMesh.vertices[indicePtr[i+2]].color;
context.draw(v_tri, 4, sf::LineStrip, sRS);
//context.draw(v_tri, 3, sf::Triangles, sRS);
}
}
}
processedVerticesCnt += oMesh.GetVerticesCount();
}
}