1
\$\begingroup\$

When Unity builds a NavMesh, you can see it in the scene view if you have the Navigation window open and select "Show NavMesh" in the Navmesh Display box. I'd like to use the exact same overlay in a camera view. Is this accessible in any way? Can it be rebuilt during runtime? Since the navMesh isn't an actual mesh, I can't seem to find a way to make this happen.

enter image description here

EDIT: This is what I'm trying to work with right now:

public class NavMeshToCamera : MonoBehaviour {

    NavMeshTriangulation triangles;
    Mesh mesh;

    void Awake () {
        triangles = NavMesh.CalculateTriangulation();
        mesh = new Mesh();

        mesh.vertices = triangles.vertices;
        mesh.triangles = triangles.indices;
    }
}

I have this script attached to a secondary camera that will be enabled while holding down a button to see an overlay of a minimap of the current dungeon. I'm not quite sure what to do with the mesh data to provide a mesh visible to the camera. Since no mesh exists initially, I suppose I'd have to take the new mesh, apply a layer to it, and have only that layer visible to the camera. But I'm having some difficulties with this.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

This should be possible by optaining the triangles from the generated NavMesh. So first you need to bake the NavMesh and then call CalculateTriangulation. This gives you a NavMeshTriangulation object which contain a list of vertices and indices. From there it should be fairly easy to transform them into a Unity Mesh. The only problem is, that the generated size of the triangulation doesn't match the size of your objects, so you have to scale the generated mesh. Strangely the scale is not uniform for all axis so for me a scalation for the x axis of 1 / 30 and for the z axis of 1 / 40 worked best. So your code should look something like this:

  var triangles = NavMesh.CalculateTriangulation ();
  Vector3[] vertices = triangles.vertices;
  for (int i = 0; i < vertices.Length; ++i) {
    vertices [i].x *= 1.0f / 30.0f;
    vertices [i].z *= 1.0f / 40.0f;
  }

  Mesh mesh = new Mesh ();
  mesh.vertices = vertices;
  mesh.triangles = triangles.indices;
  GetComponent<MeshFilter>().sharedMesh = mesh;
\$\endgroup\$
4
  • \$\begingroup\$ I've edited my question to show what I'm working with. I'm actually on that path right now, but can't quite get visualization of the mesh to work. \$\endgroup\$ Commented Aug 26, 2017 at 16:13
  • \$\begingroup\$ The problem could be the scale of your mesh, it may be to large to actually see it. You might want to try something like this Vector3[] vertices = triangles.vertices; for (int i = 0; i < vertices.Length; ++i) { vertices [i].x *= 1.0f / 30.0f; vertices [i].z *= 1.0f / 40.0f; } \$\endgroup\$
    – Tim Rolff
    Commented Aug 26, 2017 at 16:20
  • \$\begingroup\$ I ended up going a completely different route, but this was invaluable information - thank you again! \$\endgroup\$ Commented Aug 29, 2017 at 23:31
  • \$\begingroup\$ @TimRolff When I do this, I cannot apply any transparent textures, it always comes out as the solid color. \$\endgroup\$ Commented Dec 20, 2017 at 0:13
2
\$\begingroup\$

Just want to add to the answer above, if your NavMesh consists of different Areas, you can actually assign your triangle indices to different submeshes using Mesh.SetTriangles() and Mesh.subMeshCount, so that they will use different indexes from the Mesh Renderer's Materials array.

I've attached my code snippet demonstrating that below. If you want a more detailed explanation, you can refer to my article on it here: https://blog.terresquall.com/2020/07/showing-unitys-navmesh-in-game/

using UnityEngine;
using UnityEngine.AI;

public class ShowNavmesh : MonoBehaviour
{
    void Start()
    {
        ShowMesh();
    }

    // Generates the NavMesh shape and assigns it to the MeshFilter component.
    void ShowMesh()
    {
        // NavMesh.CalculateTriangulation returns a NavMeshTriangulation object.
        NavMeshTriangulation meshData = NavMesh.CalculateTriangulation();

        // Organise the NavMeshTriangulation data into Dictionary key-value pairs.
        // In this Dictionary, each area in the NavMesh will have its own list of triangles
        // that belong to the area. Each of these lists of triangles will be rendered as
        // a submesh later below.
            Dictionary<int,List<int>> submeshIndices = new Dictionary<int,List<int>>();

        // <meshData.areas> is an int[] that contains an entry for every triangle in
        // <meshData.indices>. The entry helps to identify which Navigation Area each
        // triangle belongs to.
            for(int i = 0;i < meshData.areas.Length;i++) {
                    // If a list hasn't already been created for this area index, do so.
                if(!submeshIndices.ContainsKey(meshData.areas[i])) {
                        submeshIndices.Add(meshData.areas[i],new List<int>());
                    }

            // Because a triangle contains 3 points, <meshData.indices> will always be exactly 3 times
            // the size of <meshData.areas>. Each index on <meshData.areas> identifies 3 items in <meshData.indices<,
            // so we have to identify the 3 items that each <meshData.areas< item is referring to.
                    submeshIndices[meshData.areas[i]].Add(meshData.indices[3 * i]);
                    submeshIndices[meshData.areas[i]].Add(meshData.indices[3 * i + 1]);
                    submeshIndices[meshData.areas[i]].Add(meshData.indices[3 * i + 2]);
            }

        // Create a new mesh and chuck in the NavMesh's vertex and triangle data to form the mesh.
        Mesh mesh = new Mesh();
        mesh.vertices = meshData.vertices;

        // Tell our mesh how many submeshes it contains, and use SetTriangles() to assign 
        // triangles to the different submeshes in the mesh object.
        mesh.subMeshCount = submeshIndices.Count;
        int index = 0;
        foreach(KeyValuePair<int,List<int>> entry in submeshIndices) {
            mesh.SetTriangles(entry.Value.ToArray(),index++);
            }

        // Assigns the newly-created mesh to the MeshFilter on the same GameObject.
        GetComponent<MeshFilter>().mesh = mesh;
    }
}

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .