#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 space -> world space -> camera space -> homogeneous clip space -> NDC space -> raster space // // 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 // * https://ktstephano.github.io/rendering/stratusgfx/aabbs // * https://en.wikipedia.org/wiki/Clipping_(computer_graphics) // * https://www.coranac.com/tonc/text/mode7.htm Graphic3DRenderer::Graphic3DRenderer() { if (mMainCamera == nullptr) { mMainCamera = std::make_unique(); mMainCamera->SetPosition(0.0f, 1.5f, -8.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()); mRenderList.back()->SetPosition(0.f, 0.f, 15.f); mRenderList.back()->SetScale(2.0f); mRenderList.push_back(std::make_shared()); mRenderList.back()->SetPosition(6.f, 2.f, 2.f); mRenderList.back()->SetScale(2.0f); mRenderList.push_back(std::make_shared()); mRenderList.back()->SetPosition(-8.f, 5.f, 10.f); mRenderList.back()->SetScale(2.0f); mRenderList.push_back(std::make_shared()); mRenderList.back()->SetPosition(0.f, 0.f, 0.f); mRenderList.back()->SetScale(5.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(); } }