/// <summary> /// Draws the polygon boundary lines based on the height detail. /// </summary> private static void DrawPolyBoundaries(NavmeshTileHeader header , NavmeshPoly[] polys , Vector3[] verts , NavmeshDetailMesh[] meshes , byte[] detailTris , Vector3[] detailVerts , NavmeshLink[] links , Color color , bool inner) { const float thr = 0.01f * 0.01f; for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type == NavmeshPolyType.OffMeshConnection) continue; NavmeshDetailMesh mesh = meshes[i]; Vector3[] tv = new Vector3[3]; for (int j = 0, nj = (int)poly.vertCount; j < nj; j++) { Color c = color; // Color may change. if (inner) { if (poly.neighborPolyRefs[j] == 0) continue; if ((poly.neighborPolyRefs[j] & Navmesh.ExternalLink) != 0) { bool con = false; for (uint k = poly.firstLink ; k != Navmesh.NullLink ; k = links[k].next) { if (links[k].edge == j) { con = true; break; } } if (con) c = new Color(1, 1, 1, 0.2f); else c = new Color(0, 0, 0, 0.2f); } else c = new Color(0, 0.2f, 0.25f, 0.13f); } else { if (poly.neighborPolyRefs[j] != 0) continue; } GL.Color(c); int pVertA = poly.indices[j]; int pVertB = poly.indices[(j + 1) % nj]; for (int k = 0; k < mesh.triCount; k++) { int pTri = (int)((mesh.triBase + k) * 4); for (int m = 0; m < 3; m++) { int iVert = detailTris[pTri + m]; if (iVert < poly.vertCount) { int pv = poly.indices[iVert]; tv[m] = verts[pv]; } else { int pv = (int)(mesh.vertBase + (iVert - poly.vertCount)); tv[m] = detailVerts[pv]; } } for (int m = 0, n = 2; m < 3; n = m++) { if (((detailTris[pTri + 3] >> (n * 2)) & 0x3) == 0) // Skip inner detail edges. continue; float distN = Line2.GetPointLineDistanceSq( new Vector2(tv[n].x, tv[n].z) , new Vector2(verts[pVertA].x, verts[pVertA].z) , new Vector2(verts[pVertB].x, verts[pVertB].z)); float distM = Line2.GetPointLineDistanceSq( new Vector2(tv[m].x, tv[m].z) , new Vector2(verts[pVertA].x, verts[pVertA].z) , new Vector2(verts[pVertB].x, verts[pVertB].z)); if (distN < thr && distM < thr) { GL.Vertex(tv[n]); GL.Vertex(tv[m]); } } } } } }
/// <summary> /// Draws a debug visualization of an individual navmesh tile. /// </summary> /// <remarks> /// <para> /// The tile will be checked to see if it is in use before it is drawn. So there is no /// need for caller to do so. /// </para> /// </remarks> private static void Draw(NavmeshTile tile , NavmeshQuery query, uint[] markPolys, int markPolyCount , int colorId) { NavmeshTileHeader header = tile.GetHeader(); // Keep this check. Less trouble for clients. if (header.polyCount < 1) { return; } DebugDraw.SimpleMaterial.SetPass(0); uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); NavmeshDetailMesh[] meshes = new NavmeshDetailMesh[header.detailMeshCount]; tile.GetDetailMeshes(meshes); byte[] detailTris = new byte[header.detailTriCount * 4]; tile.GetDetailTris(detailTris); Vector3[] detailVerts = new Vector3[header.detailVertCount]; tile.GetDetailVerts(detailVerts); GL.Begin(GL.TRIANGLES); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type == NavmeshPolyType.OffMeshConnection) { continue; } NavmeshDetailMesh mesh = meshes[i]; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); GL.Color(color); for (int j = 0; j < mesh.triCount; j++) { int pTri = (int)(mesh.triBase + j) * 4; for (int k = 0; k < 3; k++) { // Note: iVert and pVert refer to different // arrays. int iVert = detailTris[pTri + k]; if (iVert < poly.vertCount) { // Get the vertex from the main vertices. int pVert = poly.indices[iVert]; GL.Vertex(verts[pVert]); } else { // Get the vertex from the detail vertices. int pVert = (int) (mesh.vertBase + iVert - poly.vertCount); GL.Vertex(detailVerts[pVert]); } } } } GL.End(); NavmeshLink[] links = new NavmeshLink[header.maxLinkCount]; tile.GetLinks(links); GL.Begin(GL.LINES); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0, 0.2f, 0.25f, 0.13f) , true); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0.65f, 0.2f, 0, 0.9f) , false); if (header.connCount == 0) { GL.End(); return; } NavmeshConnection[] conns = new NavmeshConnection[header.connCount]; tile.GetConnections(conns); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type != NavmeshPolyType.OffMeshConnection) { continue; } Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); // Note: Alpha of less than one doesn't look good because connections tend to // overlay a lot of geometry, resulting is off color transitions. color.a = 1; GL.Color(color); NavmeshConnection conn = conns[i - header.connBase]; Vector3 va = verts[poly.indices[0]]; Vector3 vb = verts[poly.indices[1]]; // Check to see if start and end end-points have links. bool startSet = false; bool endSet = false; for (uint k = poly.firstLink; k != Navmesh.NullLink; k = links[k].next) { if (links[k].edge == 0) { startSet = true; } if (links[k].edge == 1) { endSet = true; } } // For linked endpoints: Draw a line between on-mesh location and endpoint, // and draw circle at the endpoint. // For un-linked endpoints: Draw a small red x-marker. if (startSet) { GL.Vertex(va); GL.Vertex(conn.endpoints[0]); DebugDraw.AppendCircle(conn.endpoints[0], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[0], 0.1f); GL.Color(color); } if (endSet) { GL.Vertex(vb); GL.Vertex(conn.endpoints[1]); DebugDraw.AppendCircle(conn.endpoints[1], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[1], 0.1f); GL.Color(color); } DebugDraw.AppendArc(conn.endpoints[0], conn.endpoints[1] , 0.25f , conn.IsBiDirectional ? 0.6f : 0 , 0.6f); } GL.End(); }
/// <summary> /// Draws a debug visualization of an individual navmesh tile. /// </summary> /// <remarks> /// <para> /// The tile will be checked to see if it is in use before it is drawn. So there is no /// need for caller to do so. /// </para> /// </remarks> private static void Draw(NavmeshTile tile , NavmeshQuery query, uint[] markPolys, int markPolyCount , int colorId) { NavmeshTileHeader header = tile.GetHeader(); // Keep this check. Less trouble for clients. if (header.polyCount < 1) return; DebugDraw.SimpleMaterial.SetPass(0); uint polyBase = tile.GetBasePolyRef(); NavmeshPoly[] polys = new NavmeshPoly[header.polyCount]; tile.GetPolys(polys); Vector3[] verts = new Vector3[header.vertCount]; tile.GetVerts(verts); NavmeshDetailMesh[] meshes = new NavmeshDetailMesh[header.detailMeshCount]; tile.GetDetailMeshes(meshes); byte[] detailTris = new byte[header.detailTriCount * 4]; tile.GetDetailTris(detailTris); Vector3[] detailVerts = new Vector3[header.detailVertCount]; tile.GetDetailVerts(detailVerts); GL.Begin(GL.TRIANGLES); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type == NavmeshPolyType.OffMeshConnection) continue; NavmeshDetailMesh mesh = meshes[i]; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); GL.Color(color); for (int j = 0; j < mesh.triCount; j++) { int pTri = (int)(mesh.triBase + j) * 4; for (int k = 0; k < 3; k++) { // Note: iVert and pVert refer to different // arrays. int iVert = detailTris[pTri + k]; if (iVert < poly.vertCount) { // Get the vertex from the main vertices. int pVert = poly.indices[iVert]; GL.Vertex(verts[pVert]); } else { // Get the vertex from the detail vertices. int pVert = (int) (mesh.vertBase + iVert - poly.vertCount); GL.Vertex(detailVerts[pVert]); } } } } GL.End(); NavmeshLink[] links = new NavmeshLink[header.maxLinkCount]; tile.GetLinks(links); GL.Begin(GL.LINES); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0, 0.2f, 0.25f, 0.13f) , true); DrawPolyBoundaries(header , polys , verts , meshes , detailTris , detailVerts , links , new Color(0.65f, 0.2f, 0, 0.9f) , false); if (header.connCount == 0) { GL.End(); return; } NavmeshConnection[] conns = new NavmeshConnection[header.connCount]; tile.GetConnections(conns); for (int i = 0; i < header.polyCount; i++) { NavmeshPoly poly = polys[i]; if (poly.Type != NavmeshPolyType.OffMeshConnection) continue; Color color = GetStandardColor(polyBase | (uint)i , poly.Area, colorId , query, markPolys, markPolyCount); // Note: Alpha of less than one doesn't look good because connections tend to // overlay a lot of geometry, resulting is off color transitions. color.a = 1; GL.Color(color); NavmeshConnection conn = conns[i - header.connBase]; Vector3 va = verts[poly.indices[0]]; Vector3 vb = verts[poly.indices[1]]; // Check to see if start and end end-points have links. bool startSet = false; bool endSet = false; for (uint k = poly.firstLink; k != Navmesh.NullLink; k = links[k].next) { if (links[k].edge == 0) startSet = true; if (links[k].edge == 1) endSet = true; } // For linked endpoints: Draw a line between on-mesh location and endpoint, // and draw circle at the endpoint. // For un-linked endpoints: Draw a small red x-marker. if (startSet) { GL.Vertex(va); GL.Vertex(conn.endpoints[0]); DebugDraw.AppendCircle(conn.endpoints[0], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[0], 0.1f); GL.Color(color); } if (endSet) { GL.Vertex(vb); GL.Vertex(conn.endpoints[1]); DebugDraw.AppendCircle(conn.endpoints[1], conn.radius); } else { GL.Color(Color.red); DebugDraw.AppendXMarker(conn.endpoints[1], 0.1f); GL.Color(color); } DebugDraw.AppendArc(conn.endpoints[0], conn.endpoints[1] , 0.25f , conn.IsBiDirectional ? 0.6f : 0 , 0.6f); } GL.End(); }