2017-02-28 13:10:29 +01:00
/*************************************************************************/
/* navigation_mesh_generator.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-13 09:45:09 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-02-28 13:10:29 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2021-05-07 12:48:56 +02:00
# include "core/math/convex_hull.h"
2021-12-16 06:15:23 +01:00
# ifndef _3D_DISABLED
# include "navigation_mesh_generator.h"
//#include "core/math/quick_hull.h"
//#include "core/math/convex_hull.h"
2019-05-23 08:37:58 +02:00
# include "core/os/thread.h"
# include "scene/3d/mesh_instance.h"
2021-12-16 06:15:23 +01:00
# include "scene/3d/multimesh_instance.h"
2019-05-23 08:37:58 +02:00
# include "scene/3d/physics_body.h"
# include "scene/resources/box_shape.h"
# include "scene/resources/capsule_shape.h"
# include "scene/resources/concave_polygon_shape.h"
# include "scene/resources/convex_polygon_shape.h"
# include "scene/resources/cylinder_shape.h"
# include "scene/resources/plane_shape.h"
# include "scene/resources/primitive_meshes.h"
# include "scene/resources/shape.h"
# include "scene/resources/sphere_shape.h"
2021-11-12 13:34:25 +01:00
# include "modules/modules_enabled.gen.h" // For csg, gridmap.
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
# include "editor/editor_node.h"
# include "editor/editor_settings.h"
# endif
2019-06-13 13:29:43 +02:00
# ifdef MODULE_CSG_ENABLED
# include "modules/csg/csg_shape.h"
# endif
2019-08-18 12:03:29 +02:00
# ifdef MODULE_GRIDMAP_ENABLED
# include "modules/gridmap/grid_map.h"
# endif
2021-12-16 06:15:23 +01:00
NavigationMeshGenerator * NavigationMeshGenerator : : singleton = NULL ;
2019-05-23 08:37:58 +02:00
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _add_vertex ( const Vector3 & p_vec3 , Vector < float > & p_vertices ) {
p_vertices . push_back ( p_vec3 . x ) ;
p_vertices . push_back ( p_vec3 . y ) ;
p_vertices . push_back ( p_vec3 . z ) ;
2017-02-28 13:10:29 +01:00
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _add_mesh ( const Ref < Mesh > & p_mesh , const Transform & p_xform , Vector < float > & p_vertices , Vector < int > & p_indices ) {
2019-12-10 05:13:02 +01:00
int current_vertex_count ;
2017-02-28 13:10:29 +01:00
for ( int i = 0 ; i < p_mesh - > get_surface_count ( ) ; i + + ) {
2021-12-16 06:15:23 +01:00
current_vertex_count = p_vertices . size ( ) / 3 ;
2017-10-09 14:14:27 +02:00
2021-05-05 12:44:11 +02:00
if ( p_mesh - > surface_get_primitive_type ( i ) ! = Mesh : : PRIMITIVE_TRIANGLES ) {
2017-02-28 13:10:29 +01:00
continue ;
2021-05-05 12:44:11 +02:00
}
2017-02-28 13:10:29 +01:00
int index_count = 0 ;
if ( p_mesh - > surface_get_format ( i ) & Mesh : : ARRAY_FORMAT_INDEX ) {
index_count = p_mesh - > surface_get_array_index_len ( i ) ;
} else {
index_count = p_mesh - > surface_get_array_len ( i ) ;
}
ERR_CONTINUE ( ( index_count = = 0 | | ( index_count % 3 ) ! = 0 ) ) ;
int face_count = index_count / 3 ;
Array a = p_mesh - > surface_get_arrays ( i ) ;
PoolVector < Vector3 > mesh_vertices = a [ Mesh : : ARRAY_VERTEX ] ;
PoolVector < Vector3 > : : Read vr = mesh_vertices . read ( ) ;
if ( p_mesh - > surface_get_format ( i ) & Mesh : : ARRAY_FORMAT_INDEX ) {
PoolVector < int > mesh_indices = a [ Mesh : : ARRAY_INDEX ] ;
PoolVector < int > : : Read ir = mesh_indices . read ( ) ;
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < mesh_vertices . size ( ) ; j + + ) {
2021-12-16 06:15:23 +01:00
_add_vertex ( p_xform . xform ( vr [ j ] ) , p_vertices ) ;
2017-02-28 13:10:29 +01:00
}
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < face_count ; j + + ) {
2017-02-28 13:10:29 +01:00
// CCW
2019-02-12 21:10:08 +01:00
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 0 ] ) ) ;
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 2 ] ) ) ;
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 1 ] ) ) ;
2017-02-28 13:10:29 +01:00
}
} else {
face_count = mesh_vertices . size ( ) / 3 ;
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < face_count ; j + + ) {
2021-12-16 06:15:23 +01:00
_add_vertex ( p_xform . xform ( vr [ j * 3 + 0 ] ) , p_vertices ) ;
_add_vertex ( p_xform . xform ( vr [ j * 3 + 2 ] ) , p_vertices ) ;
_add_vertex ( p_xform . xform ( vr [ j * 3 + 1 ] ) , p_vertices ) ;
2019-02-12 21:10:08 +01:00
p_indices . push_back ( current_vertex_count + ( j * 3 + 0 ) ) ;
p_indices . push_back ( current_vertex_count + ( j * 3 + 1 ) ) ;
p_indices . push_back ( current_vertex_count + ( j * 3 + 2 ) ) ;
2017-02-28 13:10:29 +01:00
}
}
}
}
2022-01-29 18:36:16 +01:00
void NavigationMeshGenerator : : _add_mesh_array ( const Array & p_array , const Transform & p_xform , Vector < float > & p_vertices , Vector < int > & p_indices ) {
PoolVector < Vector3 > mesh_vertices = p_array [ Mesh : : ARRAY_VERTEX ] ;
PoolVector < Vector3 > : : Read vr = mesh_vertices . read ( ) ;
PoolVector < int > mesh_indices = p_array [ Mesh : : ARRAY_INDEX ] ;
PoolVector < int > : : Read ir = mesh_indices . read ( ) ;
const int face_count = mesh_indices . size ( ) / 3 ;
const int current_vertex_count = p_vertices . size ( ) / 3 ;
for ( int j = 0 ; j < mesh_vertices . size ( ) ; j + + ) {
_add_vertex ( p_xform . xform ( vr [ j ] ) , p_vertices ) ;
}
for ( int j = 0 ; j < face_count ; j + + ) {
// CCW
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 0 ] ) ) ;
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 2 ] ) ) ;
p_indices . push_back ( current_vertex_count + ( ir [ j * 3 + 1 ] ) ) ;
}
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _add_faces ( const PoolVector3Array & p_faces , const Transform & p_xform , Vector < float > & p_vertices , Vector < int > & p_indices ) {
2019-05-23 08:37:58 +02:00
int face_count = p_faces . size ( ) / 3 ;
2021-12-16 06:15:23 +01:00
int current_vertex_count = p_vertices . size ( ) / 3 ;
2019-05-23 08:37:58 +02:00
for ( int j = 0 ; j < face_count ; j + + ) {
2021-12-16 06:15:23 +01:00
_add_vertex ( p_xform . xform ( p_faces [ j * 3 + 0 ] ) , p_vertices ) ;
_add_vertex ( p_xform . xform ( p_faces [ j * 3 + 1 ] ) , p_vertices ) ;
_add_vertex ( p_xform . xform ( p_faces [ j * 3 + 2 ] ) , p_vertices ) ;
2019-05-23 08:37:58 +02:00
p_indices . push_back ( current_vertex_count + ( j * 3 + 0 ) ) ;
p_indices . push_back ( current_vertex_count + ( j * 3 + 2 ) ) ;
p_indices . push_back ( current_vertex_count + ( j * 3 + 1 ) ) ;
}
}
2022-01-30 11:30:17 +01:00
void NavigationMeshGenerator : : _parse_geometry ( const Transform & p_navmesh_xform , Node * p_node , Vector < float > & p_vertices , Vector < int > & p_indices , int p_generate_from , uint32_t p_collision_mask , bool p_recurse_children ) {
2019-05-23 08:37:58 +02:00
if ( Object : : cast_to < MeshInstance > ( p_node ) & & p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_STATIC_COLLIDERS ) {
2017-02-28 13:10:29 +01:00
MeshInstance * mesh_instance = Object : : cast_to < MeshInstance > ( p_node ) ;
Ref < Mesh > mesh = mesh_instance - > get_mesh ( ) ;
if ( mesh . is_valid ( ) ) {
2022-01-30 11:30:17 +01:00
_add_mesh ( mesh , p_navmesh_xform * mesh_instance - > get_global_transform ( ) , p_vertices , p_indices ) ;
2021-12-16 06:15:23 +01:00
}
}
if ( Object : : cast_to < MultiMeshInstance > ( p_node ) & & p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_STATIC_COLLIDERS ) {
MultiMeshInstance * multimesh_instance = Object : : cast_to < MultiMeshInstance > ( p_node ) ;
Ref < MultiMesh > multimesh = multimesh_instance - > get_multimesh ( ) ;
2022-05-27 19:35:00 +02:00
if ( multimesh . is_valid ( ) ) {
Ref < Mesh > mesh = multimesh - > get_mesh ( ) ;
if ( mesh . is_valid ( ) ) {
int n = multimesh - > get_visible_instance_count ( ) ;
if ( n = = - 1 ) {
n = multimesh - > get_instance_count ( ) ;
}
for ( int i = 0 ; i < n ; i + + ) {
_add_mesh ( mesh , p_navmesh_xform * multimesh_instance - > get_global_transform ( ) * multimesh - > get_instance_transform ( i ) , p_vertices , p_indices ) ;
}
2021-12-16 06:15:23 +01:00
}
2017-02-28 13:10:29 +01:00
}
}
2019-06-13 13:29:43 +02:00
# ifdef MODULE_CSG_ENABLED
if ( Object : : cast_to < CSGShape > ( p_node ) & & p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_STATIC_COLLIDERS ) {
CSGShape * csg_shape = Object : : cast_to < CSGShape > ( p_node ) ;
Array meshes = csg_shape - > get_meshes ( ) ;
if ( ! meshes . empty ( ) ) {
Ref < Mesh > mesh = meshes [ 1 ] ;
if ( mesh . is_valid ( ) ) {
2022-01-30 11:30:17 +01:00
_add_mesh ( mesh , p_navmesh_xform * csg_shape - > get_global_transform ( ) , p_vertices , p_indices ) ;
2019-06-13 13:29:43 +02:00
}
}
}
# endif
2019-05-23 08:37:58 +02:00
if ( Object : : cast_to < StaticBody > ( p_node ) & & p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_MESH_INSTANCES ) {
StaticBody * static_body = Object : : cast_to < StaticBody > ( p_node ) ;
if ( static_body - > get_collision_layer ( ) & p_collision_mask ) {
2022-05-01 13:30:58 +02:00
List < uint32_t > shape_owners ;
static_body - > get_shape_owners ( & shape_owners ) ;
for ( List < uint32_t > : : Element * E = shape_owners . front ( ) ; E ; E = E - > next ( ) ) {
uint32_t shape_owner = E - > get ( ) ;
const int shape_count = static_body - > shape_owner_get_shape_count ( shape_owner ) ;
for ( int i = 0 ; i < shape_count ; i + + ) {
Ref < Shape > s = static_body - > shape_owner_get_shape ( shape_owner , i ) ;
if ( s . is_null ( ) ) {
continue ;
}
const Transform transform = p_navmesh_xform * static_body - > get_global_transform ( ) * static_body - > shape_owner_get_transform ( shape_owner ) ;
2019-05-23 08:37:58 +02:00
BoxShape * box = Object : : cast_to < BoxShape > ( * s ) ;
if ( box ) {
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CubeMesh : : create_mesh_array ( arr , box - > get_extents ( ) * 2.0 ) ;
_add_mesh_array ( arr , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
CapsuleShape * capsule = Object : : cast_to < CapsuleShape > ( * s ) ;
if ( capsule ) {
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CapsuleMesh : : create_mesh_array ( arr , capsule - > get_radius ( ) , capsule - > get_height ( ) / 2.0 ) ;
_add_mesh_array ( arr , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
CylinderShape * cylinder = Object : : cast_to < CylinderShape > ( * s ) ;
if ( cylinder ) {
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CylinderMesh : : create_mesh_array ( arr , cylinder - > get_radius ( ) , cylinder - > get_radius ( ) , cylinder - > get_height ( ) ) ;
_add_mesh_array ( arr , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
SphereShape * sphere = Object : : cast_to < SphereShape > ( * s ) ;
if ( sphere ) {
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
SphereMesh : : create_mesh_array ( arr , sphere - > get_radius ( ) , sphere - > get_radius ( ) * 2.0 ) ;
_add_mesh_array ( arr , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
ConcavePolygonShape * concave_polygon = Object : : cast_to < ConcavePolygonShape > ( * s ) ;
if ( concave_polygon ) {
2021-12-16 06:15:23 +01:00
_add_faces ( concave_polygon - > get_faces ( ) , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
ConvexPolygonShape * convex_polygon = Object : : cast_to < ConvexPolygonShape > ( * s ) ;
if ( convex_polygon ) {
Vector < Vector3 > varr = Variant ( convex_polygon - > get_points ( ) ) ;
Geometry : : MeshData md ;
2021-05-07 12:48:56 +02:00
Error err = ConvexHullComputer : : convex_hull ( varr , md ) ;
2019-05-23 08:37:58 +02:00
if ( err = = OK ) {
PoolVector3Array faces ;
for ( int j = 0 ; j < md . faces . size ( ) ; + + j ) {
Geometry : : MeshData : : Face face = md . faces [ j ] ;
for ( int k = 2 ; k < face . indices . size ( ) ; + + k ) {
faces . push_back ( md . vertices [ face . indices [ 0 ] ] ) ;
faces . push_back ( md . vertices [ face . indices [ k - 1 ] ] ) ;
faces . push_back ( md . vertices [ face . indices [ k ] ] ) ;
}
}
2021-12-16 06:15:23 +01:00
_add_faces ( faces , transform , p_vertices , p_indices ) ;
2019-05-23 08:37:58 +02:00
}
}
}
}
}
}
2019-08-18 12:03:29 +02:00
# ifdef MODULE_GRIDMAP_ENABLED
2022-01-14 16:36:59 +01:00
GridMap * gridmap = Object : : cast_to < GridMap > ( p_node ) ;
if ( gridmap ) {
if ( p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_STATIC_COLLIDERS ) {
Array meshes = gridmap - > get_meshes ( ) ;
2022-01-30 11:30:17 +01:00
Transform xform = gridmap - > get_global_transform ( ) ;
2022-01-14 16:36:59 +01:00
for ( int i = 0 ; i < meshes . size ( ) ; i + = 2 ) {
Ref < Mesh > mesh = meshes [ i + 1 ] ;
if ( mesh . is_valid ( ) ) {
Transform mesh_xform = meshes [ i ] ;
2022-01-30 11:30:17 +01:00
_add_mesh ( mesh , p_navmesh_xform * xform * mesh_xform , p_vertices , p_indices ) ;
2022-01-14 16:36:59 +01:00
}
}
}
if ( p_generate_from ! = NavigationMesh : : PARSED_GEOMETRY_MESH_INSTANCES & & ( gridmap - > get_collision_layer ( ) & p_collision_mask ) ) {
Array shapes = gridmap - > get_collision_shapes ( ) ;
for ( int i = 0 ; i < shapes . size ( ) ; i + = 2 ) {
RID shape = shapes [ i + 1 ] ;
PhysicsServer : : ShapeType type = PhysicsServer : : get_singleton ( ) - > shape_get_type ( shape ) ;
Variant data = PhysicsServer : : get_singleton ( ) - > shape_get_data ( shape ) ;
switch ( type ) {
case PhysicsServer : : SHAPE_SPHERE : {
real_t radius = data ;
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
SphereMesh : : create_mesh_array ( arr , radius , radius * 2.0 ) ;
_add_mesh_array ( arr , shapes [ i ] , p_vertices , p_indices ) ;
2022-01-14 16:36:59 +01:00
} break ;
case PhysicsServer : : SHAPE_BOX : {
Vector3 extents = data ;
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CubeMesh : : create_mesh_array ( arr , extents * 2.0 ) ;
_add_mesh_array ( arr , shapes [ i ] , p_vertices , p_indices ) ;
2022-01-14 16:36:59 +01:00
} break ;
case PhysicsServer : : SHAPE_CAPSULE : {
Dictionary dict = data ;
real_t radius = dict [ " radius " ] ;
real_t height = dict [ " height " ] ;
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CapsuleMesh : : create_mesh_array ( arr , radius , height * 0.5 ) ;
_add_mesh_array ( arr , shapes [ i ] , p_vertices , p_indices ) ;
2022-01-14 16:36:59 +01:00
} break ;
case PhysicsServer : : SHAPE_CYLINDER : {
Dictionary dict = data ;
real_t radius = dict [ " radius " ] ;
real_t height = dict [ " height " ] ;
2022-01-29 18:36:16 +01:00
Array arr ;
arr . resize ( VS : : ARRAY_MAX ) ;
CylinderMesh : : create_mesh_array ( arr , radius , radius , height ) ;
_add_mesh_array ( arr , shapes [ i ] , p_vertices , p_indices ) ;
2022-01-14 16:36:59 +01:00
} break ;
case PhysicsServer : : SHAPE_CONVEX_POLYGON : {
PoolVector3Array vertices = data ;
Geometry : : MeshData md ;
Error err = ConvexHullComputer : : convex_hull ( vertices , md ) ;
if ( err = = OK ) {
PoolVector3Array faces ;
for ( int j = 0 ; j < md . faces . size ( ) ; + + j ) {
Geometry : : MeshData : : Face face = md . faces [ j ] ;
for ( int k = 2 ; k < face . indices . size ( ) ; + + k ) {
faces . push_back ( md . vertices [ face . indices [ 0 ] ] ) ;
faces . push_back ( md . vertices [ face . indices [ k - 1 ] ] ) ;
faces . push_back ( md . vertices [ face . indices [ k ] ] ) ;
}
}
_add_faces ( faces , shapes [ i ] , p_vertices , p_indices ) ;
}
} break ;
case PhysicsServer : : SHAPE_CONCAVE_POLYGON : {
PoolVector3Array faces = data ;
_add_faces ( faces , shapes [ i ] , p_vertices , p_indices ) ;
} break ;
default : {
WARN_PRINT ( " Unsupported collision shape type. " ) ;
} break ;
}
2019-08-18 12:03:29 +02:00
}
}
}
# endif
2019-05-23 08:37:58 +02:00
2019-10-16 11:33:47 +02:00
if ( p_recurse_children ) {
for ( int i = 0 ; i < p_node - > get_child_count ( ) ; i + + ) {
2022-01-30 11:30:17 +01:00
_parse_geometry ( p_navmesh_xform , p_node - > get_child ( i ) , p_vertices , p_indices , p_generate_from , p_collision_mask , p_recurse_children ) ;
2019-10-16 11:33:47 +02:00
}
2017-02-28 13:10:29 +01:00
}
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _convert_detail_mesh_to_native_navigation_mesh ( const rcPolyMeshDetail * p_detail_mesh , Ref < NavigationMesh > p_nav_mesh ) {
2017-02-28 13:10:29 +01:00
PoolVector < Vector3 > nav_vertices ;
for ( int i = 0 ; i < p_detail_mesh - > nverts ; i + + ) {
const float * v = & p_detail_mesh - > verts [ i * 3 ] ;
nav_vertices . append ( Vector3 ( v [ 0 ] , v [ 1 ] , v [ 2 ] ) ) ;
}
p_nav_mesh - > set_vertices ( nav_vertices ) ;
for ( int i = 0 ; i < p_detail_mesh - > nmeshes ; i + + ) {
const unsigned int * m = & p_detail_mesh - > meshes [ i * 4 ] ;
const unsigned int bverts = m [ 0 ] ;
const unsigned int btris = m [ 2 ] ;
const unsigned int ntris = m [ 3 ] ;
const unsigned char * tris = & p_detail_mesh - > tris [ btris * 4 ] ;
for ( unsigned int j = 0 ; j < ntris ; j + + ) {
Vector < int > nav_indices ;
nav_indices . resize ( 3 ) ;
2019-05-10 17:33:25 +02:00
// Polygon order in recast is opposite than godot's
2018-07-25 03:11:03 +02:00
nav_indices . write [ 0 ] = ( ( int ) ( bverts + tris [ j * 4 + 0 ] ) ) ;
2019-05-10 17:33:25 +02:00
nav_indices . write [ 1 ] = ( ( int ) ( bverts + tris [ j * 4 + 2 ] ) ) ;
nav_indices . write [ 2 ] = ( ( int ) ( bverts + tris [ j * 4 + 1 ] ) ) ;
2017-02-28 13:10:29 +01:00
p_nav_mesh - > add_polygon ( nav_indices ) ;
}
}
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _build_recast_navigation_mesh (
Ref < NavigationMesh > p_nav_mesh ,
# ifdef TOOLS_ENABLED
EditorProgress * ep ,
# endif
rcHeightfield * hf ,
rcCompactHeightfield * chf ,
rcContourSet * cset ,
rcPolyMesh * poly_mesh ,
rcPolyMeshDetail * detail_mesh ,
Vector < float > & vertices ,
Vector < int > & indices ) {
2017-02-28 13:10:29 +01:00
rcContext ctx ;
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Setting up Configuration... " ) , 1 ) ;
# endif
2017-02-28 13:10:29 +01:00
2018-01-18 21:37:17 +01:00
const float * verts = vertices . ptr ( ) ;
const int nverts = vertices . size ( ) / 3 ;
2017-02-28 13:10:29 +01:00
const int * tris = indices . ptr ( ) ;
const int ntris = indices . size ( ) / 3 ;
float bmin [ 3 ] , bmax [ 3 ] ;
rcCalcBounds ( verts , nverts , bmin , bmax ) ;
rcConfig cfg ;
memset ( & cfg , 0 , sizeof ( cfg ) ) ;
cfg . cs = p_nav_mesh - > get_cell_size ( ) ;
cfg . ch = p_nav_mesh - > get_cell_height ( ) ;
cfg . walkableSlopeAngle = p_nav_mesh - > get_agent_max_slope ( ) ;
cfg . walkableHeight = ( int ) Math : : ceil ( p_nav_mesh - > get_agent_height ( ) / cfg . ch ) ;
cfg . walkableClimb = ( int ) Math : : floor ( p_nav_mesh - > get_agent_max_climb ( ) / cfg . ch ) ;
cfg . walkableRadius = ( int ) Math : : ceil ( p_nav_mesh - > get_agent_radius ( ) / cfg . cs ) ;
cfg . maxEdgeLen = ( int ) ( p_nav_mesh - > get_edge_max_length ( ) / p_nav_mesh - > get_cell_size ( ) ) ;
cfg . maxSimplificationError = p_nav_mesh - > get_edge_max_error ( ) ;
cfg . minRegionArea = ( int ) ( p_nav_mesh - > get_region_min_size ( ) * p_nav_mesh - > get_region_min_size ( ) ) ;
cfg . mergeRegionArea = ( int ) ( p_nav_mesh - > get_region_merge_size ( ) * p_nav_mesh - > get_region_merge_size ( ) ) ;
cfg . maxVertsPerPoly = ( int ) p_nav_mesh - > get_verts_per_poly ( ) ;
2022-05-15 21:52:02 +02:00
cfg . detailSampleDist = MAX ( p_nav_mesh - > get_cell_size ( ) * p_nav_mesh - > get_detail_sample_distance ( ) , 0.1f ) ;
2017-02-28 13:10:29 +01:00
cfg . detailSampleMaxError = p_nav_mesh - > get_cell_height ( ) * p_nav_mesh - > get_detail_sample_max_error ( ) ;
cfg . bmin [ 0 ] = bmin [ 0 ] ;
cfg . bmin [ 1 ] = bmin [ 1 ] ;
cfg . bmin [ 2 ] = bmin [ 2 ] ;
cfg . bmax [ 0 ] = bmax [ 0 ] ;
cfg . bmax [ 1 ] = bmax [ 1 ] ;
cfg . bmax [ 2 ] = bmax [ 2 ] ;
2022-06-23 13:54:41 +02:00
AABB baking_aabb = p_nav_mesh - > get_filter_baking_aabb ( ) ;
bool aabb_has_no_volume = baking_aabb . has_no_area ( ) ;
if ( ! aabb_has_no_volume ) {
Vector3 baking_aabb_offset = p_nav_mesh - > get_filter_baking_aabb_offset ( ) ;
cfg . bmin [ 0 ] = baking_aabb . position [ 0 ] + baking_aabb_offset . x ;
cfg . bmin [ 1 ] = baking_aabb . position [ 1 ] + baking_aabb_offset . y ;
cfg . bmin [ 2 ] = baking_aabb . position [ 2 ] + baking_aabb_offset . z ;
cfg . bmax [ 0 ] = cfg . bmin [ 0 ] + baking_aabb . size [ 0 ] ;
cfg . bmax [ 1 ] = cfg . bmin [ 1 ] + baking_aabb . size [ 1 ] ;
cfg . bmax [ 2 ] = cfg . bmin [ 2 ] + baking_aabb . size [ 2 ] ;
}
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Calculating grid size... " ) , 2 ) ;
# endif
2017-02-28 13:10:29 +01:00
rcCalcGridSize ( cfg . bmin , cfg . bmax , cfg . cs , & cfg . width , & cfg . height ) ;
2022-05-11 15:36:50 +02:00
// ~30000000 seems to be around sweetspot where Editor baking breaks
if ( ( cfg . width * cfg . height ) > 30000000 ) {
WARN_PRINT ( " NavigationMesh baking process will likely fail. "
" \n Source geometry is suspiciously big for the current Cell Size and Cell Height in the NavMesh Resource bake settings. "
" \n If baking does not fail, the resulting NavigationMesh will create serious pathfinding performance issues. "
" \n It is advised to increase Cell Size and/or Cell Height in the NavMesh Resource bake settings or reduce the size / scale of the source geometry. " ) ;
}
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Creating heightfield... " ) , 3 ) ;
# endif
2017-02-28 13:10:29 +01:00
hf = rcAllocHeightfield ( ) ;
ERR_FAIL_COND ( ! hf ) ;
ERR_FAIL_COND ( ! rcCreateHeightfield ( & ctx , * hf , cfg . width , cfg . height , cfg . bmin , cfg . bmax , cfg . cs , cfg . ch ) ) ;
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Marking walkable triangles... " ) , 4 ) ;
# endif
2017-02-28 13:10:29 +01:00
{
Vector < unsigned char > tri_areas ;
tri_areas . resize ( ntris ) ;
ERR_FAIL_COND ( tri_areas . size ( ) = = 0 ) ;
2017-11-25 04:07:54 +01:00
memset ( tri_areas . ptrw ( ) , 0 , ntris * sizeof ( unsigned char ) ) ;
rcMarkWalkableTriangles ( & ctx , cfg . walkableSlopeAngle , verts , nverts , tris , ntris , tri_areas . ptrw ( ) ) ;
2017-02-28 13:10:29 +01:00
ERR_FAIL_COND ( ! rcRasterizeTriangles ( & ctx , verts , nverts , tris , tri_areas . ptr ( ) , ntris , * hf , cfg . walkableClimb ) ) ;
}
2021-05-05 12:44:11 +02:00
if ( p_nav_mesh - > get_filter_low_hanging_obstacles ( ) ) {
2017-02-28 13:10:29 +01:00
rcFilterLowHangingWalkableObstacles ( & ctx , cfg . walkableClimb , * hf ) ;
2021-05-05 12:44:11 +02:00
}
if ( p_nav_mesh - > get_filter_ledge_spans ( ) ) {
2017-02-28 13:10:29 +01:00
rcFilterLedgeSpans ( & ctx , cfg . walkableHeight , cfg . walkableClimb , * hf ) ;
2021-05-05 12:44:11 +02:00
}
if ( p_nav_mesh - > get_filter_walkable_low_height_spans ( ) ) {
2017-02-28 13:10:29 +01:00
rcFilterWalkableLowHeightSpans ( & ctx , cfg . walkableHeight , * hf ) ;
2021-05-05 12:44:11 +02:00
}
2017-02-28 13:10:29 +01:00
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Constructing compact heightfield... " ) , 5 ) ;
# endif
2017-02-28 13:10:29 +01:00
chf = rcAllocCompactHeightfield ( ) ;
ERR_FAIL_COND ( ! chf ) ;
ERR_FAIL_COND ( ! rcBuildCompactHeightfield ( & ctx , cfg . walkableHeight , cfg . walkableClimb , * hf , * chf ) ) ;
rcFreeHeightField ( hf ) ;
2021-12-16 06:15:23 +01:00
hf = 0 ;
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Eroding walkable area... " ) , 6 ) ;
# endif
2017-02-28 13:10:29 +01:00
ERR_FAIL_COND ( ! rcErodeWalkableArea ( & ctx , cfg . walkableRadius , * chf ) ) ;
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Partitioning... " ) , 7 ) ;
# endif
2017-02-28 13:10:29 +01:00
if ( p_nav_mesh - > get_sample_partition_type ( ) = = NavigationMesh : : SAMPLE_PARTITION_WATERSHED ) {
ERR_FAIL_COND ( ! rcBuildDistanceField ( & ctx , * chf ) ) ;
ERR_FAIL_COND ( ! rcBuildRegions ( & ctx , * chf , 0 , cfg . minRegionArea , cfg . mergeRegionArea ) ) ;
} else if ( p_nav_mesh - > get_sample_partition_type ( ) = = NavigationMesh : : SAMPLE_PARTITION_MONOTONE ) {
ERR_FAIL_COND ( ! rcBuildRegionsMonotone ( & ctx , * chf , 0 , cfg . minRegionArea , cfg . mergeRegionArea ) ) ;
} else {
ERR_FAIL_COND ( ! rcBuildLayerRegions ( & ctx , * chf , 0 , cfg . minRegionArea ) ) ;
}
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Creating contours... " ) , 8 ) ;
# endif
2017-02-28 13:10:29 +01:00
cset = rcAllocContourSet ( ) ;
ERR_FAIL_COND ( ! cset ) ;
ERR_FAIL_COND ( ! rcBuildContours ( & ctx , * chf , cfg . maxSimplificationError , cfg . maxEdgeLen , * cset ) ) ;
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Creating polymesh... " ) , 9 ) ;
# endif
2017-02-28 13:10:29 +01:00
poly_mesh = rcAllocPolyMesh ( ) ;
ERR_FAIL_COND ( ! poly_mesh ) ;
ERR_FAIL_COND ( ! rcBuildPolyMesh ( & ctx , * cset , cfg . maxVertsPerPoly , * poly_mesh ) ) ;
detail_mesh = rcAllocPolyMeshDetail ( ) ;
ERR_FAIL_COND ( ! detail_mesh ) ;
ERR_FAIL_COND ( ! rcBuildPolyMeshDetail ( & ctx , * poly_mesh , * chf , cfg . detailSampleDist , cfg . detailSampleMaxError , * detail_mesh ) ) ;
rcFreeCompactHeightfield ( chf ) ;
2021-12-16 06:15:23 +01:00
chf = 0 ;
2017-02-28 13:10:29 +01:00
rcFreeContourSet ( cset ) ;
2021-12-16 06:15:23 +01:00
cset = 0 ;
2017-02-28 13:10:29 +01:00
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Converting to native navigation mesh... " ) , 10 ) ;
# endif
2017-02-28 13:10:29 +01:00
_convert_detail_mesh_to_native_navigation_mesh ( detail_mesh , p_nav_mesh ) ;
rcFreePolyMesh ( poly_mesh ) ;
2021-12-16 06:15:23 +01:00
poly_mesh = 0 ;
2017-02-28 13:10:29 +01:00
rcFreePolyMeshDetail ( detail_mesh ) ;
2021-12-16 06:15:23 +01:00
detail_mesh = 0 ;
2017-02-28 13:10:29 +01:00
}
2021-12-16 06:15:23 +01:00
NavigationMeshGenerator * NavigationMeshGenerator : : get_singleton ( ) {
2019-05-23 08:37:58 +02:00
return singleton ;
}
2021-12-16 06:15:23 +01:00
NavigationMeshGenerator : : NavigationMeshGenerator ( ) {
2019-05-23 08:37:58 +02:00
singleton = this ;
}
2021-12-16 06:15:23 +01:00
NavigationMeshGenerator : : ~ NavigationMeshGenerator ( ) {
2019-05-23 08:37:58 +02:00
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : bake ( Ref < NavigationMesh > p_nav_mesh , Node * p_node ) {
ERR_FAIL_COND_MSG ( ! p_nav_mesh . is_valid ( ) , " Invalid Navigation Mesh " ) ;
2020-02-23 02:23:27 +01:00
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
2022-06-22 00:49:02 +02:00
EditorProgress * ep ( nullptr ) ;
// FIXME
# endif
#if 0
// After discussion on devchat disabled EditorProgress for now as it is not thread-safe and uses hacks and Main::iteration() for steps.
// EditorProgress randomly crashes the Engine when the bake function is used with a thread e.g. inside Editor with a tool script and procedural navigation
// This was not a problem in older versions as previously Godot was unable to (re)bake NavigationMesh at runtime.
// If EditorProgress is fixed and made thread-safe this should be enabled again.
2021-12-16 06:15:23 +01:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
ep = memnew ( EditorProgress ( " bake " , TTR ( " Navigation Mesh Generator Setup: " ) , 11 ) ) ;
}
2017-02-28 13:10:29 +01:00
2021-12-16 06:15:23 +01:00
if ( ep )
ep - > step ( TTR ( " Parsing Geometry... " ) , 0 ) ;
# endif
2017-02-28 13:10:29 +01:00
2018-01-18 21:37:17 +01:00
Vector < float > vertices ;
2017-02-28 13:10:29 +01:00
Vector < int > indices ;
2019-10-16 11:33:47 +02:00
List < Node * > parse_nodes ;
if ( p_nav_mesh - > get_source_geometry_mode ( ) = = NavigationMesh : : SOURCE_GEOMETRY_NAVMESH_CHILDREN ) {
parse_nodes . push_back ( p_node ) ;
} else {
p_node - > get_tree ( ) - > get_nodes_in_group ( p_nav_mesh - > get_source_group_name ( ) , & parse_nodes ) ;
}
2022-01-30 11:30:17 +01:00
Transform navmesh_xform = Object : : cast_to < Spatial > ( p_node ) - > get_global_transform ( ) . affine_inverse ( ) ;
2019-10-16 11:33:47 +02:00
for ( const List < Node * > : : Element * E = parse_nodes . front ( ) ; E ; E = E - > next ( ) ) {
2021-08-11 10:22:44 +02:00
NavigationMesh : : ParsedGeometryType geometry_type = p_nav_mesh - > get_parsed_geometry_type ( ) ;
2019-10-16 11:33:47 +02:00
uint32_t collision_mask = p_nav_mesh - > get_collision_mask ( ) ;
bool recurse_children = p_nav_mesh - > get_source_geometry_mode ( ) ! = NavigationMesh : : SOURCE_GEOMETRY_GROUPS_EXPLICIT ;
_parse_geometry ( navmesh_xform , E - > get ( ) , vertices , indices , geometry_type , collision_mask , recurse_children ) ;
}
2017-02-28 13:10:29 +01:00
2018-01-18 21:37:17 +01:00
if ( vertices . size ( ) > 0 & & indices . size ( ) > 0 ) {
2021-05-04 16:00:45 +02:00
rcHeightfield * hf = nullptr ;
rcCompactHeightfield * chf = nullptr ;
rcContourSet * cset = nullptr ;
rcPolyMesh * poly_mesh = nullptr ;
rcPolyMeshDetail * detail_mesh = nullptr ;
2017-02-28 13:10:29 +01:00
2021-12-16 06:15:23 +01:00
_build_recast_navigation_mesh (
p_nav_mesh ,
# ifdef TOOLS_ENABLED
ep ,
# endif
hf ,
chf ,
cset ,
poly_mesh ,
detail_mesh ,
vertices ,
indices ) ;
2017-02-28 13:10:29 +01:00
2018-04-21 16:35:23 +02:00
rcFreeHeightField ( hf ) ;
2021-12-16 06:15:23 +01:00
hf = 0 ;
2018-04-21 16:35:23 +02:00
rcFreeCompactHeightfield ( chf ) ;
2021-12-16 06:15:23 +01:00
chf = 0 ;
2018-04-21 16:35:23 +02:00
rcFreeContourSet ( cset ) ;
2021-12-16 06:15:23 +01:00
cset = 0 ;
2018-04-21 16:35:23 +02:00
rcFreePolyMesh ( poly_mesh ) ;
2021-12-16 06:15:23 +01:00
poly_mesh = 0 ;
2018-04-21 16:35:23 +02:00
rcFreePolyMeshDetail ( detail_mesh ) ;
2021-12-16 06:15:23 +01:00
detail_mesh = 0 ;
2017-02-28 13:10:29 +01:00
}
2021-12-16 06:15:23 +01:00
# ifdef TOOLS_ENABLED
if ( ep )
ep - > step ( TTR ( " Done! " ) , 11 ) ;
if ( ep )
memdelete ( ep ) ;
# endif
2022-01-25 23:16:50 +01:00
p_nav_mesh - > property_list_changed_notify ( ) ;
2017-02-28 13:10:29 +01:00
}
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : clear ( Ref < NavigationMesh > p_nav_mesh ) {
2017-02-28 13:10:29 +01:00
if ( p_nav_mesh . is_valid ( ) ) {
p_nav_mesh - > clear_polygons ( ) ;
p_nav_mesh - > set_vertices ( PoolVector < Vector3 > ( ) ) ;
}
}
2019-05-23 08:37:58 +02:00
2021-12-16 06:15:23 +01:00
void NavigationMeshGenerator : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " bake " , " nav_mesh " , " root_node " ) , & NavigationMeshGenerator : : bake ) ;
ClassDB : : bind_method ( D_METHOD ( " clear " , " nav_mesh " ) , & NavigationMeshGenerator : : clear ) ;
2019-05-23 08:37:58 +02:00
}
2021-12-16 06:15:23 +01:00
# endif