-
-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add doc for 2D & 3D navigationmesh generation
Adds documentation how to create navigationmesh / navpolygons for 2D and 3D both with editor tools as well with scripts + some technical background stuff.
- Loading branch information
Showing
5 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ Navigation | |
:name: toc-learn-features-navigation | ||
|
||
real_time_navigation_3d | ||
navigation_using_navigationmeshes |
325 changes: 325 additions & 0 deletions
325
tutorials/navigation/navigation_using_navigationmeshes.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,325 @@ | ||
.. _doc_navigation_using_navigationmeshes: | ||
|
||
Using NavigationMeshes | ||
====================== | ||
|
||
.. warning:: | ||
|
||
This chapter is a stub for linking and unfinished. | ||
|
||
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 resource 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 geometry parsing | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
NavigationMesh resources have the following properties and parameters to control the geometry parsing behavior: | ||
|
||
- ``SamplePartitionType`` | ||
Defines the geometry parsing algorithm and which steps of the baking processes are used or left out. | ||
|
||
- ``SAMPLE_PARTITION_WATERSHED`` | ||
Prepares region partitioning with holes and calculates distance field along walkable surfaces. | ||
The most complex and detailed of the SamplePartitionTypes. Slow and requires more memory but results in highest quality navmesh. | ||
|
||
Default and recommended for all surfaces. Takes some time to bake. | ||
|
||
- ``SAMPLE_PARTITION_MONOTONE`` | ||
Partitions the walkable surface into simple regions without holes. | ||
|
||
Only recommened for simple surfaces. Less detail but faster. | ||
|
||
- ``SAMPLE_PARTITION_LAYERS`` | ||
Partition the walkable surface into simple regions without holes. | ||
|
||
Only recommended for flat and very simple surfaces. Lowest detail but very fast. | ||
|
||
|
||
- ``ParsedGeometryTypes`` | ||
Defines which geometry from the Godot scene is added or ignored for the navigationmesh bake. | ||
|
||
- ``PARSED_GEOMETRY_MESH_INSTANCES`` | ||
|
||
Only visual meshes from Nodes are parsed. | ||
|
||
This includes: | ||
- MeshInstance3D | ||
- MultiMeshInstance3D (only visible_instance_count meshes) | ||
- CSGShape3D without collision | ||
- GridMap cell meshes | ||
|
||
- ``PARSED_GEOMETRY_STATIC_COLLIDERS`` | ||
Only static Collisionobjects from Nodes are parsed. | ||
|
||
This includes: | ||
- StaticBody3D | ||
- CSGShape3D with collision | ||
- GridMap cell static collisions | ||
|
||
- ``PARSED_GEOMETRY_BOTH`` | ||
Both visual meshes and static Collisionobjects from Nodes are parsed. | ||
|
||
This includes: | ||
- MeshInstance3D | ||
- MultiMeshInstance3D (only visible_instance_count) | ||
- CSGShape3D with or without collision | ||
- GridMap cell meshes and static collisions | ||
- StaticBody3D | ||
|
||
- ``SourceGeometryMode`` | ||
Defines where the geometry parsing looks for Godot scene nodes. | ||
|
||
- ``SOURCE_GEOMETRY_NAVMESH_CHILDREN`` | ||
|
||
Default - Parses all childnodes of the NavigationRegion3D and adds their geometry to the bake if they fit the current ParsedGeometryType. | ||
|
||
- ``SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN`` | ||
|
||
Parses all Nodes and their childrens in the specified groupname under ``geometry/source_group_name`` | ||
and adds their geometry to the bake if they fit the current ParsedGeometryType. | ||
|
||
- ``SOURCE_GEOMETRY_GROUPS_EXPLICIT`` | ||
|
||
Parses only Nodes in the specified groupname under ``geometry/source_group_name`` | ||
and adds their geometry to the bake if they fit the current ParsedGeometryType. | ||
|
||
NavigationMesh rebaking at runtime | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
To rebake a ``NavigationMesh`` at runtime use the NavigationRegion3D.bake_navigation_mesh() function. | ||
Another option is to use 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 restriction 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) |