From 098345409f108dabe280033c09a537174aa65617 Mon Sep 17 00:00:00 2001 From: JackCarterSmith Date: Sat, 2 Nov 2024 00:41:43 +0100 Subject: [PATCH] Clipping and backface culling Culling isn't bad, clipping need a better approach to cut partial triangles. --- Engine/Graphics/3DRenderer.cpp | 88 +++++++++++++++++++++++----------- Engine/Utils/3DMaths.hpp | 3 ++ Engine/Utils/3DMaths_vec.inl | 28 ++++++++--- 3 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Engine/Graphics/3DRenderer.cpp b/Engine/Graphics/3DRenderer.cpp index 46f1fe9..2e575d4 100644 --- a/Engine/Graphics/3DRenderer.cpp +++ b/Engine/Graphics/3DRenderer.cpp @@ -3,6 +3,8 @@ #include "../World/DbgCube.hpp" #include "../World/Tank.hpp" +//#define DISABLE_INVISIBLE_VERTICES + // 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) @@ -23,6 +25,8 @@ // * https://en.wikipedia.org/wiki/Hidden-surface_determination#Occlusion_culling // * https://en.wikipedia.org/wiki/Bounding_volume_hierarchy +static bool VertexIsInsideClipSpace(M3D_F4& V); + Graphic3DRenderer::Graphic3DRenderer() { mRTSize = {1280.f, 324.f}; @@ -109,20 +113,23 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) { // Object outside frustrum clipping projAABB.Transform(projAABB, oTMat); M3D_ContainmentType objInFrustrum = camFrustrum.Contains(projAABB); - if (objInFrustrum != DISJOINT) { +#ifndef DISABLE_INVISIBLE_VERTICES + if (objInFrustrum != DISJOINT) +#endif + { size_t vCount = obj->GetObjectVerticesCount(); auto& oMesh = obj->GetObjectMesh(); - M3D_F3 projVertices[vCount]; + M3D_F4 projVertices[vCount]; - // Vertices NDC space transformation - M3D_V3TransformPersDiv( - projVertices, sizeof(M3D_F3), + // Vertices homogeneous clip space transformation + M3D_V3Transform( + projVertices, sizeof(M3D_F4), reinterpret_cast(oMesh.vertices.data()), sizeof(Vertex), vCount, oTMat * viewProjMat ); - // Draw the object indice triangles + // Draw the object indice triangles if visible or partially clipped sf::Vertex v_tri[4]; for (auto& objPt : oMesh.parts) { auto indicePtr = static_cast(objPt.indices.data()); @@ -134,31 +141,49 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) { // Triangle clipping //TODO: implement complete Cohen-Sutherland algo or similar - M3D_VECTOR V1 = M3D_V4LoadF3(&projVertices[indicePtr[i]]); - M3D_VECTOR V2 = M3D_V4LoadF3(&projVertices[indicePtr[i+1]]); - M3D_VECTOR V3 = M3D_V4LoadF3(&projVertices[indicePtr[i+2]]); + if (VertexIsInsideClipSpace(projVertices[indicePtr[i]]) && + VertexIsInsideClipSpace(projVertices[indicePtr[i+1]]) && + VertexIsInsideClipSpace(projVertices[indicePtr[i+2]])) + { + + M3D_VECTOR V1 = M3D_V4LoadF4(&projVertices[indicePtr[i]]); + M3D_VECTOR V2 = M3D_V4LoadF4(&projVertices[indicePtr[i+1]]); + M3D_VECTOR V3 = M3D_V4LoadF4(&projVertices[indicePtr[i+2]]); - V1 = M3D_V3TransformNDCToViewport(V1, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); - V2 = M3D_V3TransformNDCToViewport(V2, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); - V3 = M3D_V3TransformNDCToViewport(V3, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); + // Face culling + M3D_VECTOR faceNormal = M3D_TNormal(V1,V2,V3); + if (M3D_V4GetX(M3D_V3Dot(V1, faceNormal)) >= 0) { + // Do the perspective divide + V1 = M3D_V4Divide(V1, M3D_V4SplatW(V1)); + V2 = M3D_V4Divide(V2, M3D_V4SplatW(V2)); + V3 = M3D_V4Divide(V3, M3D_V4SplatW(V3)); - sf::Color vColor; - if (objInFrustrum == DISJOINT) - vColor = sf::Color::Red; - else if (objInFrustrum == INTERSECTS) - vColor = sf::Color::Yellow; - else - vColor = oMesh.vertices[indicePtr[i]].color; + V1 = M3D_V3TransformNDCToViewport(V1, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); + V2 = M3D_V3TransformNDCToViewport(V2, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); + V3 = M3D_V3TransformNDCToViewport(V3, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f); - v_tri[0].position = sf::Vector2f(M3D_V4GetX(V1), M3D_V4GetY(V1)); - v_tri[0].color = vColor; - v_tri[3] = v_tri[0]; - v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2)); - v_tri[1].color = vColor; - v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3)); - v_tri[2].color = vColor; - context.draw(v_tri, 4, sf::LineStrip, sRS); - //context.draw(v_tri, 3, sf::Triangles, sRS); + if (objInFrustrum == DISJOINT) { + v_tri[0].color = sf::Color::Red; + v_tri[1].color = sf::Color::Red; + v_tri[2].color = sf::Color::Red; + } else if (objInFrustrum == INTERSECTS) { + v_tri[0].color = sf::Color::Yellow; + v_tri[1].color = sf::Color::Yellow; + v_tri[2].color = sf::Color::Yellow; + } else { + v_tri[0].color = oMesh.vertices[indicePtr[i]].color; + v_tri[1].color = oMesh.vertices[indicePtr[i+1]].color; + v_tri[2].color = oMesh.vertices[indicePtr[i+2]].color; + } + + v_tri[0].position = sf::Vector2f(M3D_V4GetX(V1), M3D_V4GetY(V1)); + v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2)); + v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3)); + v_tri[3] = v_tri[0]; + context.draw(v_tri, 4, sf::LineStrip, sRS); + //context.draw(v_tri, 3, sf::Triangles, sRS); + } + } } } } @@ -176,4 +201,11 @@ void Graphic3DRenderer::UpdateInternalTestObjects() { 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); +} + +inline static bool VertexIsInsideClipSpace(M3D_F4& V) { + return (V.x > -V.w && V.x < V.w && + V.y > -V.w && V.y < V.w && + V.z > 0 && V.z < V.w + ); } \ No newline at end of file diff --git a/Engine/Utils/3DMaths.hpp b/Engine/Utils/3DMaths.hpp index ffccaf1..0d6142e 100644 --- a/Engine/Utils/3DMaths.hpp +++ b/Engine/Utils/3DMaths.hpp @@ -401,6 +401,7 @@ M3D_VECTOR M3D_V4OrInt(M3D_VECTOR V1, M3D_VECTOR V2) noexcept; M3D_VECTOR M3D_V4Reciprocal(M3D_VECTOR V) noexcept; M3D_VECTOR M3D_V4Sqrt(M3D_VECTOR V) noexcept; M3D_VECTOR M3D_V4ModAngles(M3D_VECTOR Angles) noexcept; +bool M3D_V3LessOrEqual(M3D_VECTOR V1, M3D_VECTOR V2) noexcept; M3D_VECTOR M3D_V3Dot(M3D_VECTOR V1, M3D_VECTOR V2) noexcept; M3D_VECTOR M3D_V3Cross(M3D_VECTOR V1, M3D_VECTOR V2) noexcept; M3D_VECTOR M3D_V3LengthSq(M3D_VECTOR V) noexcept; @@ -524,6 +525,8 @@ inline M3D_VECTOR M3D_V4Swizzle(M3D_VECTOR V) noexcept { M3D_VECTOR M3D_QMultiply(M3D_VECTOR Q1, M3D_VECTOR Q2) noexcept; M3D_VECTOR M3D_QConjugate(M3D_VECTOR Q) noexcept; +M3D_VECTOR M3D_TNormal(M3D_VECTOR P1, M3D_VECTOR P2, M3D_VECTOR P3) noexcept; + void M3D_V4SinCos(M3D_VECTOR* pSin, M3D_VECTOR* pCos, M3D_VECTOR V) noexcept; diff --git a/Engine/Utils/3DMaths_vec.inl b/Engine/Utils/3DMaths_vec.inl index 453849b..bd259de 100644 --- a/Engine/Utils/3DMaths_vec.inl +++ b/Engine/Utils/3DMaths_vec.inl @@ -1048,6 +1048,15 @@ inline M3D_VECTOR M3D_V4ModAngles(M3D_VECTOR Angles) noexcept { #endif } +inline bool M3D_V3LessOrEqual(M3D_VECTOR V1, M3D_VECTOR V2) noexcept { +#ifdef DISABLE_INTRINSICS + return (((V1.v4f[0] <= V2.v4f[0]) && (V1.v4f[1] <= V2.v4f[1]) && (V1.v4f[2] <= V2.v4f[2])) != 0); +#else + M3D_VECTOR vTemp = _mm_cmple_ps(V1, V2); + return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0); +#endif +} + inline M3D_VECTOR M3D_V3Dot(M3D_VECTOR V1, M3D_VECTOR V2) noexcept { #ifdef DISABLE_INTRINSICS float fValue = V1.v4f[0] * V2.v4f[0] + V1.v4f[1] * V2.v4f[1] + V1.v4f[2] * V2.v4f[2]; @@ -1244,12 +1253,12 @@ inline M3D_VECTOR M3D_V3Normalize(M3D_VECTOR V) noexcept { inline M3D_VECTOR M3D_QMultiply(M3D_VECTOR Q1, M3D_VECTOR Q2) noexcept { #ifdef DISABLE_INTRINSICS - M3D_V4F32 Result = { { { + M3D_V4F32 Result = {{{ (Q2.v4f[3] * Q1.v4f[0]) + (Q2.v4f[0] * Q1.v4f[3]) + (Q2.v4f[1] * Q1.v4f[2]) - (Q2.v4f[2] * Q1.v4f[1]), (Q2.v4f[3] * Q1.v4f[1]) - (Q2.v4f[0] * Q1.v4f[2]) + (Q2.v4f[1] * Q1.v4f[3]) + (Q2.v4f[2] * Q1.v4f[0]), (Q2.v4f[3] * Q1.v4f[2]) + (Q2.v4f[0] * Q1.v4f[1]) - (Q2.v4f[1] * Q1.v4f[0]) + (Q2.v4f[2] * Q1.v4f[3]), (Q2.v4f[3] * Q1.v4f[3]) - (Q2.v4f[0] * Q1.v4f[0]) - (Q2.v4f[1] * Q1.v4f[1]) - (Q2.v4f[2] * Q1.v4f[2]) - } } }; + }}}; return Result.v; #else static const M3D_V4F32 ControlWZYX = {{{1.0f, -1.0f, 1.0f, -1.0f}}}; @@ -1304,24 +1313,31 @@ inline M3D_VECTOR M3D_QConjugate(M3D_VECTOR Q) noexcept { #endif } +inline M3D_VECTOR M3D_TNormal(M3D_VECTOR P1, M3D_VECTOR P2, M3D_VECTOR P3) noexcept { + M3D_VECTOR L1 = M3D_V4Subtract(P2, P1); + M3D_VECTOR L2 = M3D_V4Subtract(P3, P1); + + return M3D_V3Normalize(M3D_V3Cross(L2, L1)); +} + /* -------------------------------------------------------------------------------------------------------------------------- */ inline void M3D_V4SinCos(M3D_VECTOR* pSin, M3D_VECTOR* pCos, M3D_VECTOR V) noexcept { #ifdef DISABLE_INTRINSICS - M3D_V4F32 Sin = { { { + M3D_V4F32 Sin = {{{ sinf(V.v4f[0]), sinf(V.v4f[1]), sinf(V.v4f[2]), sinf(V.v4f[3]) - } } }; + }}}; - M3D_V4F32 Cos = { { { + M3D_V4F32 Cos = {{{ cosf(V.v4f[0]), cosf(V.v4f[1]), cosf(V.v4f[2]), cosf(V.v4f[3]) - } } }; + }}}; *pSin = Sin.v; *pCos = Cos.v;