From c850d52d1b035c9957c058c74ceeb33656eeb22d Mon Sep 17 00:00:00 2001
From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com>
Date: Thu, 21 Jul 2022 02:21:59 +0200
Subject: [PATCH] Add NavMesh poly bounds debug overlay

---
 r5dev/engine/debugoverlay.cpp                 | 119 +++++++++++++++---
 .../DebugUtils/Source/DetourDebugDraw.cpp     |  15 ---
 .../recast/Detour/Include/DetourCommon.h      |   2 +
 .../recast/Detour/Source/DetourCommon.cpp     |  14 +++
 r5dev/tier1/IConVar.cpp                       |  10 +-
 r5dev/tier1/cvar.cpp                          |   2 +
 r5dev/tier1/cvar.h                            |   2 +
 7 files changed, 130 insertions(+), 34 deletions(-)

diff --git a/r5dev/engine/debugoverlay.cpp b/r5dev/engine/debugoverlay.cpp
index c7bea1af..67b5e5ca 100644
--- a/r5dev/engine/debugoverlay.cpp
+++ b/r5dev/engine/debugoverlay.cpp
@@ -19,6 +19,7 @@
 #include "game/server/ai_utility.h"
 #include "game/server/ai_network.h"
 #include "game/server/ai_networkmanager.h"
+#include "thirdparty/recast/detour/include/detourcommon.h"
 #endif // !CLIENT_DLL
 
 
@@ -382,6 +383,100 @@ static void DrawNavMeshPortals()
 #endif // !CLIENT_DLL
 }
 
+static void DrawNavMeshPolyBoundaries()
+{
+#ifndef CLIENT_DLL
+    static const float thr = 0.01f * 0.01f;
+    Color col{0, 140, 240, 255};
+
+    //dd->begin(DU_DRAW_LINES, linew);
+
+    const dtNavMesh* mesh = GetNavMeshForHull(navmesh_debug_type->GetInt());
+    if (!mesh)
+        return;
+
+    OverlayBox_t::Transforms vTransforms;
+    for (int i = navmesh_draw_poly_bounds->GetInt(); i < mesh->getTileCount(); ++i)
+    {
+        const dtMeshTile* tile = &mesh->m_tiles[i];
+        if (!tile->header)
+            continue;
+
+        for (int i = 0; i < tile->header->polyCount; ++i)
+        {
+            const dtPoly* p = &tile->polys[i];
+
+            if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
+                continue;
+
+            const dtPolyDetail* pd = &tile->detailMeshes[i];
+
+            for (int j = 0, nj = (int)p->vertCount; j < nj; ++j)
+            {
+                Color c = col;
+                if (navmesh_draw_poly_inner->GetBool())
+                {
+                    if (p->neis[j] == 0)
+                        continue;
+
+                    if (p->neis[j] & DT_EXT_LINK)
+                    {
+                        bool con = false;
+                        for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
+                        {
+                            if (tile->links[k].edge == j)
+                            {
+                                con = true;
+                                break;
+                            }
+                        }
+                        if (con)
+                            c = Color(255, 255, 255, 48);
+                        else
+                            c = Color(0, 0, 0, 48);
+                    }
+                    else
+                        c = Color(0, 48, 64, 32);
+                }
+                else
+                {
+                    if (p->neis[j] != 0) continue;
+                }
+
+                const float* v0 = &tile->verts[p->verts[j] * 3];
+                const float* v1 = &tile->verts[p->verts[(j + 1) % nj] * 3];
+
+                // Draw detail mesh edges which align with the actual poly edge.
+                // This is really slow.
+                for (int k = 0; k < pd->triCount; ++k)
+                {
+                    const unsigned char* t = &tile->detailTris[(pd->triBase + k) * 4];
+                    const float* tv[3];
+                    for (int m = 0; m < 3; ++m)
+                    {
+                        if (t[m] < p->vertCount)
+                            tv[m] = &tile->verts[p->verts[t[m]] * 3];
+                        else
+                            tv[m] = &tile->detailVerts[(pd->vertBase + (t[m] - p->vertCount)) * 3];
+                    }
+                    for (int m = 0, n = 2; m < 3; n = m++)
+                    {
+                        if ((dtGetDetailTriEdgeFlags(t[3], n) & DT_DETAIL_EDGE_BOUNDARY) == 0)
+                            continue;
+
+                        if (distancePtLine2d(tv[n], v0, v1) < thr &&
+                            distancePtLine2d(tv[m], v0, v1) < thr)
+                        {
+                            v_RenderLine(Vector3D(tv[n][0], tv[n][1], tv[n][2]), Vector3D(tv[m][0], tv[m][1], tv[m][2]), c, r_debug_overlay_zbuffer->GetBool());
+                        }
+                    }
+                }
+            }
+        }
+    }
+#endif // !CLIENT_DLL
+}
+
 //------------------------------------------------------------------------------
 // Purpose : overlay drawing entrypoint
 // Input  : bDraw - 
@@ -389,24 +484,18 @@ static void DrawNavMeshPortals()
 void DrawAllOverlays(bool bDraw)
 {
     if (!enable_debug_overlays->GetBool())
-    {
         return;
-    }
-    if (ai_script_nodes_draw->GetBool())
-    {
-        DrawAIScriptNodes();
-    }
-    if (navmesh_draw_bvtree->GetInt() > -1)
-    {
-        DrawNavMeshBVTree();
-    }
-    if (navmesh_draw_portal->GetInt() > -1)
-    {
-        DrawNavMeshPortals();
-    }
-
     EnterCriticalSection(&*s_OverlayMutex);
 
+    if (ai_script_nodes_draw->GetBool())
+        DrawAIScriptNodes();
+    if (navmesh_draw_bvtree->GetInt() > -1)
+        DrawNavMeshBVTree();
+    if (navmesh_draw_portal->GetInt() > -1)
+        DrawNavMeshPortals();
+    if (navmesh_draw_poly_bounds->GetInt() > -1)
+        DrawNavMeshPolyBoundaries();
+
     OverlayBase_t* pCurrOverlay = *s_pOverlays; // rdi
     OverlayBase_t* pPrevOverlay = nullptr;      // rsi
     OverlayBase_t* pNextOverlay = nullptr;      // rbx
diff --git a/r5dev/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp b/r5dev/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp
index f6235eef..188f9307 100644
--- a/r5dev/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp
+++ b/r5dev/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp
@@ -22,21 +22,6 @@
 #include "Detour/Include/DetourCommon.h"
 #include "Detour/Include/DetourNode.h"
 
-
-static float distancePtLine2d(const float* pt, const float* p, const float* q)
-{
-	float pqx = q[0] - p[0];
-	float pqz = q[2] - p[2];
-	float dx = pt[0] - p[0];
-	float dz = pt[2] - p[2];
-	float d = pqx*pqx + pqz*pqz;
-	float t = pqx*dx + pqz*dz;
-	if (d != 0) t /= d;
-	dx = p[0] + t*pqx - pt[0];
-	dz = p[2] + t*pqz - pt[2];
-	return dx*dx + dz*dz;
-}
-
 static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile,
 							   const unsigned int col, const float linew,
 							   bool inner)
diff --git a/r5dev/thirdparty/recast/Detour/Include/DetourCommon.h b/r5dev/thirdparty/recast/Detour/Include/DetourCommon.h
index da8c2b13..428831bf 100644
--- a/r5dev/thirdparty/recast/Detour/Include/DetourCommon.h
+++ b/r5dev/thirdparty/recast/Detour/Include/DetourCommon.h
@@ -405,6 +405,8 @@ bool dtIntersectSegSeg2D(const float* ap, const float* aq,
 						 const float* bp, const float* bq,
 						 float& s, float& t);
 
+float distancePtLine2d(const float* pt, const float* p, const float* q);
+
 /// Determines if the specified point is inside the convex polygon on the xz-plane.
 ///  @param[in]		pt		The point to check. [(x, y, z)]
 ///  @param[in]		verts	The polygon vertices. [(x, y, z) * @p nverts]
diff --git a/r5dev/thirdparty/recast/Detour/Source/DetourCommon.cpp b/r5dev/thirdparty/recast/Detour/Source/DetourCommon.cpp
index 0614bd70..c4441723 100644
--- a/r5dev/thirdparty/recast/Detour/Source/DetourCommon.cpp
+++ b/r5dev/thirdparty/recast/Detour/Source/DetourCommon.cpp
@@ -385,3 +385,17 @@ bool dtIntersectSegSeg2D(const float* ap, const float* aq,
 	return true;
 }
 
+float distancePtLine2d(const float* pt, const float* p, const float* q)
+{
+	float pqx = q[0] - p[0];
+	float pqz = q[2] - p[2];
+	float dx = pt[0] - p[0];
+	float dz = pt[2] - p[2];
+	float d = pqx * pqx + pqz * pqz;
+	float t = pqx * dx + pqz * dz;
+	if (d != 0) t /= d;
+	dx = p[0] + t * pqx - pt[0];
+	dz = p[2] + t * pqz - pt[2];
+	return dx * dx + dz * dz;
+}
+
diff --git a/r5dev/tier1/IConVar.cpp b/r5dev/tier1/IConVar.cpp
index 297c7e1c..e2eda1f6 100644
--- a/r5dev/tier1/IConVar.cpp
+++ b/r5dev/tier1/IConVar.cpp
@@ -65,10 +65,12 @@ void ConVar::Init(void) const
 	ai_ainDebugConnect         = new ConVar("ai_ainDebugConnect"        , "0", FCVAR_DEVELOPMENTONLY, "Debug AIN node connections.", false, 0.f, false, 0.f, nullptr, nullptr);
 	ai_script_nodes_draw_index = new ConVar("ai_script_nodes_draw_index", "0", FCVAR_DEVELOPMENTONLY, "Start index for drawing script nodes.", false, 0.f, false, 0.f, nullptr, nullptr);
 
-	navmesh_always_reachable   = new ConVar("navmesh_always_reachable"  , "0", FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr);
-	navmesh_debug_type         = new ConVar("navmesh_debug_type"  , "0", FCVAR_DEVELOPMENTONLY, "NavMesh hull index for debug draw.", true, 0.f, true, 4.f, nullptr, "0 = small, 1 = med_short, 2 = medium, 3 = large, 4 = extra large");
-	navmesh_draw_bvtree        = new ConVar("navmesh_draw_bvtree"  , "-1", FCVAR_DEVELOPMENTONLY, "Draws the BVTree of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
-	navmesh_draw_portal        = new ConVar("navmesh_draw_portal"  , "-1", FCVAR_DEVELOPMENTONLY, "Draws the portal of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
+	navmesh_always_reachable   = new ConVar("navmesh_always_reachable" , "0" , FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr);
+	navmesh_debug_type         = new ConVar("navmesh_debug_type"       , "0" , FCVAR_DEVELOPMENTONLY, "NavMesh hull index for debug draw.", true, 0.f, true, 4.f, nullptr, "0 = small, 1 = med_short, 2 = medium, 3 = large, 4 = extra large");
+	navmesh_draw_bvtree        = new ConVar("navmesh_draw_bvtree"      , "-1", FCVAR_DEVELOPMENTONLY, "Draws the BVTree of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
+	navmesh_draw_portal        = new ConVar("navmesh_draw_portal"      , "-1", FCVAR_DEVELOPMENTONLY, "Draws the portal of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
+	navmesh_draw_poly_bounds   = new ConVar("navmesh_draw_poly_bounds" , "-1", FCVAR_DEVELOPMENTONLY, "Draws the bounds of the NavMesh polys.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
+	navmesh_draw_poly_inner    = new ConVar("navmesh_draw_poly_inner"  , "0" , FCVAR_DEVELOPMENTONLY, "Draws the inner bounds of the NavMesh polys (requires navmesh_draw_poly_bounds).", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount");
 
 	sv_showconnecting  = new ConVar("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr);
 	sv_pylonVisibility = new ConVar("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visiblity to the Pylon master server, 0 = Offline, 1 = Hidden, 2 = Public.", false, 0.f, false, 0.f, nullptr, nullptr);
diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp
index a9b754ef..2d1fdaac 100644
--- a/r5dev/tier1/cvar.cpp
+++ b/r5dev/tier1/cvar.cpp
@@ -51,6 +51,8 @@ ConVar* navmesh_always_reachable           = nullptr;
 ConVar* navmesh_debug_type                 = nullptr;
 ConVar* navmesh_draw_bvtree                = nullptr;
 ConVar* navmesh_draw_portal                = nullptr;
+ConVar* navmesh_draw_poly_bounds           = nullptr;
+ConVar* navmesh_draw_poly_inner            = nullptr;
 
 ConVar* sv_showconnecting                  = nullptr;
 ConVar* sv_pylonVisibility                 = nullptr;
diff --git a/r5dev/tier1/cvar.h b/r5dev/tier1/cvar.h
index 92998f17..3b62b3db 100644
--- a/r5dev/tier1/cvar.h
+++ b/r5dev/tier1/cvar.h
@@ -50,6 +50,8 @@ extern ConVar* navmesh_always_reachable;
 extern ConVar* navmesh_debug_type;
 extern ConVar* navmesh_draw_bvtree;
 extern ConVar* navmesh_draw_portal;
+extern ConVar* navmesh_draw_poly_bounds;
+extern ConVar* navmesh_draw_poly_inner;
 
 extern ConVar* sv_showconnecting;
 extern ConVar* sv_pylonVisibility;