Search Unity

Combining Meshes at Runtime for NavMesh Generation

Discussion in 'Navigation' started by DFXLuna, Mar 11, 2018.

  1. DFXLuna

    DFXLuna

    Joined:
    Mar 3, 2015
    Posts:
    2
    Hey all,
    For the current project I'm working on, I need to generate a navmesh for navigation of a city landscape. I've got the navmesh set up to generate at runtime but I'm having an issue due to one of my outside libraries. I'm using mapbox to generate meshes and creates two meshes, one for the buildings and one for the underlying map, like this:
    The underlying map

    The buildings layered on top

    The objects are structured so that there's a parent gameobject with one child, the map, and the map has one child, the buildings.

    The main issue I'm trying to solve is that when I generate the navmesh, it runs under the buildings and my navmesh agents can't route around them.

    Things I've tried:
    - Marking the buildings as Navmesh obstacles
    The obstacle bounding boxes were too large and so navigating through alleys was impossible

    - Apply Navmesh Modifier Volume to the buildings

    - Combining the meshes

    When I try to combine the meshes, I get some very strange behavior where only the rooftops are visible, like this:


    I'm not too worried about the material but I am confused as to why my buildings are missing walls.

    The meshes are combined with a couple of code snippets, the first is a script that gets attached to the buildings when they are generated by mapbox. All it does is call a meshcombiner object on the building's parent(The map tile):

    Code (CSharp):
    1.         MeshCombiner mc = GameObject.FindGameObjectWithTag( "MeshCombiner" ).GetComponent<MeshCombiner>();
    2.         mc.ApplyToMapTile( gameObject.transform.parent.gameObject );
    And the ApplyToMapTile function looks like this:

    Code (CSharp):
    1. public void ApplyToMapTile( GameObject tile ){
    2.         CombineInstance[] meshes = new CombineInstance[3];
    3.        
    4.         // grab tile mesh
    5.         MeshFilter tmf = tile.GetComponent<MeshFilter>();
    6.        
    7.         meshes[0].mesh = tmf.mesh;
    8.         meshes[0].transform = tmf.transform.localToWorldMatrix;
    9.  
    10.         // grab building mesh
    11.         MeshFilter[] bmf = tile.GetComponentsInChildren<MeshFilter>( true );
    12.  
    13.         meshes[1].mesh = bmf[0].mesh;
    14.         meshes[1].transform = bmf[0].transform.localToWorldMatrix;
    15.         meshes[2].mesh = bmf[1].mesh;
    16.         meshes[2].transform = bmf[1].transform.localToWorldMatrix;
    17.  
    18.         // Then deactive the building
    19.         foreach( Transform child in tile.transform ){
    20.             if( child.gameObject.name == "building" ){
    21.                 child.gameObject.SetActive( false );
    22.             }
    23.         }
    24.  
    25.         meshHolder = new Mesh();
    26.         meshHolder.CombineMeshes( meshes, true );
    27.         tile.GetComponent<MeshFilter>().mesh = meshHolder;
    28.  
    All of the code is available in the github repo here

    So, I have two main questions
    1. Is combining the meshes before generating the navmesh a reasonable solution to original navmesh problem ( The navmesh going under buildings and making it unusable ) ? Is there a better solution?
    2. What am I doing wrong combining the meshes to end up with just the rooftops?

    Thanks all
     
  2. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,983
    why are you doing this:
    Code (CSharp):
    1.   meshes[0].mesh = tmf.mesh;
    2.         meshes[0].transform = tmf.transform.localToWorldMatrix;
    why the specific transformation from local to world space, and what exactly are you trying to achieve with it?

    This seems entirely unnecessary for what your trying to do, just:

    Code (CSharp):
    1. meshes[0].mesh = tmf.mesh;
    should suffice
     
  3. DFXLuna

    DFXLuna

    Joined:
    Mar 3, 2015
    Posts:
    2
    I was just following the example given in the docs. All of the tutorials I've seen have it so I assumed it was necessary.

    Thanks for the input!