Browse Source

Merge pull request #6048 from smix8/doc_navigation_meshes_n_procedural_4.x

Add doc for 2D & 3D navigationmesh generation
Max Hilbrunner 2 years ago
parent
commit
225c9fc557

BIN
tutorials/navigation/img/nav_polydrawtool.png


BIN
tutorials/navigation/img/nav_polymatroschka.png


BIN
tutorials/navigation/img/nav_polyoutlinefail.png


+ 1 - 0
tutorials/navigation/index.rst

@@ -11,3 +11,4 @@ Navigation
    navigation_different_actor_types
    navigation_different_actor_locomotion
    navigation_using_navigationobstacles
+   navigation_using_navigationmeshes

+ 246 - 0
tutorials/navigation/navigation_using_navigationmeshes.rst

@@ -0,0 +1,246 @@
+.. _doc_navigation_using_navigationmeshes:
+
+Using NavigationMeshes
+======================
+
+2D and 3D version of the navigation mesh are available as 
+:ref:`NavigationPolygon<class_NavigationPolygon>` and 
+:ref:`NavigationMesh<class_NavigationMesh>`  respectively.
+
+.. _doc_navigation_navmesh_baking:
+
+Creating 2D NavigationMeshes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Navigation meshes in the 2D editor are created with the help of the NavigationPolygon draw tools 
+that appear in the top bar of the editor when a NavigationRegion2D is selected.
+
+.. image:: img/nav_polydrawtool.png
+
+The NavigationPolygon draw tools can be used to create and edit navigation meshes by defining ``outline`` polygons.
+The outline polygons are later converted to real NavigationMesh resources for the NavigationServer regions.
+
+.. image:: img/nav_polymatroschka.png
+
+Multiple outlines can be added to the same NavPolygon resource as long as they **do not intersect or overlap**.
+Each additional outline will cut a hole in the polygon created by the larger outline.
+If the larger polygon is already a hole it will create a new navigation mesh polygon inside.
+
+Outlines are not a replacement if the intention is to merge aligned polygons e.g. from grid cells.
+Outlines, as the name would suggest, cannot intersect each other or have any overlapping vertex positions.
+
+.. image:: img/nav_polyoutlinefail.png
+
+Outline layouts like seen in this picture will fail the convex partitioning required by the navigation mesh generation.
+In this layout cases the outline tool cannot be used. Use the :ref:`Geometry2D<class_Geometry2D>` class for 
+polygon merge or intersect operations to create a valid merged mesh for navigation.
+
+.. note::
+
+    The NavigationServer does not connect navigation mesh islands from the same NavigationMesh resource. 
+    Do not create multiple disconnected islands in the same NavigationRegion2D and NavPoly resource if they should be later connected.
+
+For 2D no similar navigation mesh baking with geometry parsing exists like in 3D.
+The Geometry2D class functions for offset, merge, intersect and clip can be used 
+to shrink or enlarge existing NavigationPolygons to different actor sizes.
+
+Creating 3D NavigationMeshes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. image:: img/baked_navmesh.png
+
+Navigation meshes in the 3D editor are created with the help of the 
+:ref:`NavigationMeshGenerator<class_NavigationMeshGenerator>` singleton 
+and the NavigationMesh bake settings that appear in the editor inspector.
+
+NavigationMesh baking is the process of creating a simplified mesh used for pathfinding out of (complex) 3D level geometry.
+For this process Godot parses scene geometry and hands the raw mesh or collision data to the 
+third-party ReCast library for processing and creation of the final navigationmesh.
+
+The resulting NavigationMesh is an approximation of the source geometry surfaces 
+for both performance and technical reasons. Do not expect the NavigationMesh 
+to perfectly follow the original surfaces. Especially navigation polygons placed 
+over ramps will not keep an equal distance to the ground surface. To align an 
+actor perfectly with the ground use other means like physics.
+
+.. warning::
+
+    Meshes need to be triangulated to work as navigation meshes. Other mesh face formats like quad or ngon are not supported.
+
+NavigationMesh rebaking at runtime
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To rebake a ``NavigationMesh`` at runtime, use the NavigationRegion3D.bake_navigation_mesh() function.
+Another option is to use the NavigationMeshGenerator.bake() Singleton function with the NavigationMesh resource directly.
+If the navmesh resource is already prepared, the region can be updated with the NavigationServer3D API directly as well.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    
+    extends NavigationRegion3D
+    
+    func update_navmesh():
+        
+        # use bake and update function of region
+        var on_thread : bool = true
+        bake_navigation_mesh(on_thread)
+        
+        # or use the NavigationMeshGenerator Singleton
+        var navigationmesh : NavigationMesh = navmesh
+        NavigationMeshGenerator.bake(navigationmesh, self)
+        # remove old resource first to trigger a full update
+        navmesh = null
+        navmesh = navigationmesh
+        
+        # or use NavigationServer API to update region with prepared navmesh
+        var region_rid : RID = get_region_rid()
+        NavigationServer3D.region_set_navmesh(region_rid, navmesh)
+
+.. note::
+
+    Baking a NavigationMesh at runtime is a costly operation.
+    Complex navmesh take some time to bake and if done on the main thread can freeze a game.
+    (Re)baking a large navmesh is preferably done in a separate thread.
+
+.. warning::
+
+    Property values on a NavigationMesh resource like ``cell_size`` need 
+    to match the actual mesh data stored inside in order to merge 
+    different navigation meshes without issues.
+
+NavigationRegion2D and Navigation3D both use meshes to mark traversable areas, only the tools to create them are different.
+
+For 2D NavigationPolygon resources are used to draw outline points in the editor. From these outline points the NavigationServer2D creates a mesh to upload navigation data to the NavigationServer.
+
+For 3D NavigationMesh resources are used. Instead of providing draw tools the 3D variant 
+provides an extensive amount of parameters to bake a navigation mesh directly from 3D source geometry.
+
+.. note::
+
+    Technically there is no hard distinction between 2D and 3D how to use the given toolsets to create flat navigation meshes. The 2D drawing tool can be used to create a flat 3D navmesh and the 3D baking tool can be used to parse flat 3D geometry into 2D appropriated navigationmeshes.
+
+2D Navmesh from CollisionPolygons
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following script parses all child nodes of a NavigationRegion2D for CollisionPolygons 
+and bakes their shape into the NavigationPolygon. As the NavigationPolygon creates the 
+navigationmesh from outline data the shapes cannot overlap.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends NavigationRegion2D
+    
+    var navigationpolygon : NavigationPolygon = get_navigation_polygon()
+    
+    func _ready():
+        
+        parse_2d_collisionshapes(self)
+        
+        navigationpolygon.make_polygons_from_outlines()
+        set_navigation_polygon(navigationpolygon)
+    
+    func parse_2d_collisionshapes(root_node : Node2D):
+        
+        for node in root_node.get_children():
+            
+            if node.get_child_count() > 0:
+                parse_2d_collisionshapes(node)
+            
+            if node is CollisionPolygon2D:
+                
+                var new_collision_outline : PackedVector2Array = PackedVector2Array()
+                var collisionpolygon_transform : Transform2D = node.get_global_transform()
+                var collisionpolygon : CollisionPolygon2D = node.get_polygon()
+                
+                for vertex in collisionpolygon:
+                    new_collision_outline.append(collisionpolygon_transform.xform(vertex))
+                
+                navigationpolygon.add_outline(new_collision_outline)
+
+Procedual 2D Navmesh
+~~~~~~~~~~~~~~~~~~~~
+
+The following script creates a new 2D navigation region and fills it with procedual generated navmesh data from a NavigationPolygon resource.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Node2D
+    
+    var new_2d_region_rid : RID = NavigationServer2D.region_create()
+
+    var default_2d_map_rid : RID = get_world_2d().get_navigation_map()
+    NavigationServer2D.region_set_map(new_2d_region_rid, default_2d_map_rid)
+
+    var new_navpoly : NavigationPolygon = NavigationPolygon.new()
+    var new_outline : PackedVector2Array = PackedVector2Array([
+        Vector2(0.0, 0.0),
+        Vector2(50.0, 0.0),
+        Vector2(50.0, 50.0),
+        Vector2(0.0, 50.0),
+        ])
+    new_navpoly.add_outline(new_outline)
+    new_navpoly.make_polygons_from_outlines()
+    
+    NavigationServer2D.region_set_navpoly(new_2d_region_rid, new_navpoly)
+
+Procedual 3D Navmesh
+~~~~~~~~~~~~~~~~~~~~
+
+The following script creates a new 3D navigation region and fills it with procedual generated navmesh data from a NavigationMesh resource.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Node3D
+    
+    var new_3d_region_rid : RID = NavigationServer3D.region_create()
+    
+    var default_3d_map_rid : RID = get_world_3d().get_navigation_map()
+    NavigationServer3D.region_set_map(new_3d_region_rid, default_3d_map_rid)
+    
+    var new_navmesh : NavigationMesh = NavigationMesh.new()
+    var new_plane_mesh : PlaneMesh = PlaneMesh.new()
+    new_plane_mesh.size = Vector2(10.0, 10.0)
+    new_navmesh.create_from_mesh(new_plane_mesh)
+    
+    NavigationServer3D.region_set_navmesh(new_3d_region_rid, new_navmesh)
+
+Navmesh for 3D GridMaps
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The following script creates a new 3D navmesh from the mesh of a GridMap item, clears the current grid cells and adds new procedual grid cells with the new navmesh.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends GridMap
+    
+    # enable navmesh for grid items
+    set_bake_navigation(true)
+    
+    # get mesh from grid item, bake and set a new navmesh for the library
+    var gridmap_item_list : PackedInt32Array = mesh_library.get_item_list()
+    for item in gridmap_item_list:
+        var item_mesh : Mesh = mesh_library.get_item_mesh(item)
+        var navmesh : NavigationMesh = NavigationMesh.new()
+        navmesh.create_from_mesh(item_mesh)
+        mesh_library.set_item_navmesh(item, item_mesh)
+        mesh_library.set_item_navmesh_transform(item, Transform3D())
+    
+    # clear the cells
+    clear()
+    
+    # add procedual cells using the first item
+    var _position : Vector3i = Vector3i(global_transform.origin)
+    var _item : int = 0
+    var _orientation : int = 0
+    for i in range(0,10):
+        for j in range(0,10):
+            _position.x = i
+            _position.z = j
+            gridmap.set_cell_item(_position, _item, _orientation)
+            _position.x = -i
+            _position.z = -j
+            gridmap.set_cell_item(_position, _item, _orientation)