From 1add4c15abed368876a2ff35a6e0aca789d16703 Mon Sep 17 00:00:00 2001 From: jfons Date: Thu, 23 May 2019 08:37:58 +0200 Subject: [PATCH] Various improvements to NavigationMesh generation * Expose EditorNavigationMeshGenerator as an engine singleton so users can generate navmesehes from `tool` scripts. * Add support for generating navmeshes from static colliders. All collision shapes are supported except for Plane (since Plane is an infinite collider and navmeshes need to have finite geometry). * When using static colliders as a geometry source, a layer mask can be specified to ignore certain colliders. * Don't rely on global transform. It still should give the exact same results but allows for building navmeshes on nodes that are not in the tree (useful in `tool` scripts). * Update navigation gizmos after every new bake. This work has been kindly sponsored by IMVU. --- .../recast/navigation_mesh_editor_plugin.cpp | 12 +- modules/recast/navigation_mesh_generator.cpp | 164 ++++++++++++++++-- modules/recast/navigation_mesh_generator.h | 24 ++- modules/recast/register_types.cpp | 17 +- scene/3d/navigation_mesh.cpp | 84 ++++++++- scene/3d/navigation_mesh.h | 21 +++ 6 files changed, 296 insertions(+), 26 deletions(-) diff --git a/modules/recast/navigation_mesh_editor_plugin.cpp b/modules/recast/navigation_mesh_editor_plugin.cpp index 62108620bd8..eadc11fcee1 100644 --- a/modules/recast/navigation_mesh_editor_plugin.cpp +++ b/modules/recast/navigation_mesh_editor_plugin.cpp @@ -54,26 +54,28 @@ void NavigationMeshEditor::_notification(int p_option) { } void NavigationMeshEditor::_bake_pressed() { + button_bake->set_pressed(false); ERR_FAIL_COND(!node); const String conf_warning = node->get_configuration_warning(); if (!conf_warning.empty()) { err_dialog->set_text(conf_warning); err_dialog->popup_centered_minsize(); - button_bake->set_pressed(false); return; } - NavigationMeshGenerator::clear(node->get_navigation_mesh()); - NavigationMeshGenerator::bake(node->get_navigation_mesh(), node); + EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); + EditorNavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node); - node->update_gizmo(); + if (node) { + node->update_gizmo(); + } } void NavigationMeshEditor::_clear_pressed() { if (node) - NavigationMeshGenerator::clear(node->get_navigation_mesh()); + EditorNavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh()); button_bake->set_pressed(false); bake_info->set_text(""); diff --git a/modules/recast/navigation_mesh_generator.cpp b/modules/recast/navigation_mesh_generator.cpp index 79ccbbb0309..0cac07e3e72 100644 --- a/modules/recast/navigation_mesh_generator.cpp +++ b/modules/recast/navigation_mesh_generator.cpp @@ -29,14 +29,31 @@ /*************************************************************************/ #include "navigation_mesh_generator.h" +#include "core/math/quick_hull.h" +#include "core/os/thread.h" +#include "editor/editor_settings.h" +#include "scene/3d/collision_shape.h" +#include "scene/3d/mesh_instance.h" +#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" -void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector &p_verticies) { +EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL; + +void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector &p_verticies) { p_verticies.push_back(p_vec3.x); p_verticies.push_back(p_vec3.y); p_verticies.push_back(p_vec3.z); } -void NavigationMeshGenerator::_add_mesh(const Ref &p_mesh, const Transform &p_xform, Vector &p_verticies, Vector &p_indices) { +void EditorNavigationMeshGenerator::_add_mesh(const Ref &p_mesh, const Transform &p_xform, Vector &p_verticies, Vector &p_indices) { int current_vertex_count = 0; for (int i = 0; i < p_mesh->get_surface_count(); i++) { @@ -91,23 +108,132 @@ void NavigationMeshGenerator::_add_mesh(const Ref &p_mesh, const Transform } } -void NavigationMeshGenerator::_parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector &p_verticies, Vector &p_indices) { +void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector &p_verticies, Vector &p_indices) { + int face_count = p_faces.size() / 3; + int current_vertex_count = p_verticies.size() / 3; - if (Object::cast_to(p_node)) { + for (int j = 0; j < face_count; j++) { + _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_verticies); + _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_verticies); + _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_verticies); + + 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)); + } +} + +void EditorNavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector &p_verticies, Vector &p_indices, int p_generate_from, uint32_t p_collision_mask) { + + if (Object::cast_to(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) { MeshInstance *mesh_instance = Object::cast_to(p_node); Ref mesh = mesh_instance->get_mesh(); if (mesh.is_valid()) { - _add_mesh(mesh, p_base_inverse * mesh_instance->get_global_transform(), p_verticies, p_indices); + _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_verticies, p_indices); } } + if (Object::cast_to(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_MESH_INSTANCES) { + StaticBody *static_body = Object::cast_to(p_node); + + if (static_body->get_collision_layer() & p_collision_mask) { + + for (int i = 0; i < p_node->get_child_count(); ++i) { + Node *child = p_node->get_child(i); + if (Object::cast_to(child)) { + CollisionShape *col_shape = Object::cast_to(child); + + Transform transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform(); + + Ref mesh; + Ref s = col_shape->get_shape(); + + BoxShape *box = Object::cast_to(*s); + if (box) { + Ref cube_mesh; + cube_mesh.instance(); + cube_mesh->set_size(box->get_extents() * 2.0); + mesh = cube_mesh; + } + + CapsuleShape *capsule = Object::cast_to(*s); + if (capsule) { + Ref capsule_mesh; + capsule_mesh.instance(); + capsule_mesh->set_radius(capsule->get_radius()); + capsule_mesh->set_mid_height(capsule->get_height() / 2.0); + mesh = capsule_mesh; + } + + CylinderShape *cylinder = Object::cast_to(*s); + if (cylinder) { + Ref cylinder_mesh; + cylinder_mesh.instance(); + cylinder_mesh->set_height(cylinder->get_height()); + cylinder_mesh->set_bottom_radius(cylinder->get_radius()); + cylinder_mesh->set_top_radius(cylinder->get_radius()); + mesh = cylinder_mesh; + } + + SphereShape *sphere = Object::cast_to(*s); + if (sphere) { + Ref sphere_mesh; + sphere_mesh.instance(); + sphere_mesh->set_radius(sphere->get_radius()); + sphere_mesh->set_height(sphere->get_radius() * 2.0); + mesh = sphere_mesh; + } + + ConcavePolygonShape *concave_polygon = Object::cast_to(*s); + if (concave_polygon) { + _add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices); + } + + ConvexPolygonShape *convex_polygon = Object::cast_to(*s); + if (convex_polygon) { + Vector varr = Variant(convex_polygon->get_points()); + Geometry::MeshData md; + + Error err = QuickHull::build(varr, 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, transform, p_verticies, p_indices); + } + } + + if (mesh.is_valid()) { + _add_mesh(mesh, transform, p_verticies, p_indices); + } + } + } + } + } + + if (Object::cast_to(p_node)) { + + Spatial *spatial = Object::cast_to(p_node); + p_accumulated_transform = p_accumulated_transform * spatial->get_transform(); + } + for (int i = 0; i < p_node->get_child_count(); i++) { - _parse_geometry(p_base_inverse, p_node->get_child(i), p_verticies, p_indices); + _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask); } } -void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref p_nav_mesh) { +void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref p_nav_mesh) { PoolVector nav_vertices; @@ -135,7 +261,7 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con } } -void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref p_nav_mesh, EditorProgress *ep, +void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref p_nav_mesh, EditorProgress *ep, rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh, Vector &vertices, Vector &indices) { rcContext ctx; @@ -257,7 +383,18 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref detail_mesh = 0; } -void NavigationMeshGenerator::bake(Ref p_nav_mesh, Node *p_node) { +EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() { + return singleton; +} + +EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() { + singleton = this; +} + +EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() { +} + +void EditorNavigationMeshGenerator::bake(Ref p_nav_mesh, Node *p_node) { ERR_FAIL_COND(!p_nav_mesh.is_valid()); @@ -267,7 +404,7 @@ void NavigationMeshGenerator::bake(Ref p_nav_mesh, Node *p_node) Vector vertices; Vector indices; - _parse_geometry(Object::cast_to(p_node)->get_global_transform().affine_inverse(), p_node, vertices, indices); + _parse_geometry(Object::cast_to(p_node)->get_transform().affine_inverse(), p_node, vertices, indices, p_nav_mesh->get_parsed_geometry_type(), p_nav_mesh->get_collision_mask()); if (vertices.size() > 0 && indices.size() > 0) { @@ -297,9 +434,14 @@ void NavigationMeshGenerator::bake(Ref p_nav_mesh, Node *p_node) ep.step(TTR("Done!"), 11); } -void NavigationMeshGenerator::clear(Ref p_nav_mesh) { +void EditorNavigationMeshGenerator::clear(Ref p_nav_mesh) { if (p_nav_mesh.is_valid()) { p_nav_mesh->clear_polygons(); p_nav_mesh->set_vertices(PoolVector()); } } + +void EditorNavigationMeshGenerator::_bind_methods() { + ClassDB::bind_method(D_METHOD("bake", "nav_mesh", "root_node"), &EditorNavigationMeshGenerator::bake); + ClassDB::bind_method(D_METHOD("clear", "nav_mesh"), &EditorNavigationMeshGenerator::clear); +} diff --git a/modules/recast/navigation_mesh_generator.h b/modules/recast/navigation_mesh_generator.h index 3adc01ccda5..30a6e3c835a 100644 --- a/modules/recast/navigation_mesh_generator.h +++ b/modules/recast/navigation_mesh_generator.h @@ -31,20 +31,23 @@ #ifndef NAVIGATION_MESH_GENERATOR_H #define NAVIGATION_MESH_GENERATOR_H -#include "core/os/thread.h" #include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "scene/3d/mesh_instance.h" #include "scene/3d/navigation_mesh.h" -#include "scene/resources/shape.h" #include -class NavigationMeshGenerator { +class EditorNavigationMeshGenerator : public Object { + GDCLASS(EditorNavigationMeshGenerator, Object); + + static EditorNavigationMeshGenerator *singleton; + protected: + static void _bind_methods(); + static void _add_vertex(const Vector3 &p_vec3, Vector &p_verticies); static void _add_mesh(const Ref &p_mesh, const Transform &p_xform, Vector &p_verticies, Vector &p_indices); - static void _parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector &p_verticies, Vector &p_indices); + static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector &p_verticies, Vector &p_indices); + static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector &p_verticies, Vector &p_indices, int p_generate_from, uint32_t p_collision_mask); static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref p_nav_mesh); static void _build_recast_navigation_mesh(Ref p_nav_mesh, EditorProgress *ep, @@ -52,8 +55,13 @@ protected: rcPolyMeshDetail *detail_mesh, Vector &vertices, Vector &indices); public: - static void bake(Ref p_nav_mesh, Node *p_node); - static void clear(Ref p_nav_mesh); + static EditorNavigationMeshGenerator *get_singleton(); + + EditorNavigationMeshGenerator(); + ~EditorNavigationMeshGenerator(); + + void bake(Ref p_nav_mesh, Node *p_node); + void clear(Ref p_nav_mesh); }; #endif // NAVIGATION_MESH_GENERATOR_H diff --git a/modules/recast/register_types.cpp b/modules/recast/register_types.cpp index f272cc4236b..247d7f61440 100644 --- a/modules/recast/register_types.cpp +++ b/modules/recast/register_types.cpp @@ -32,8 +32,23 @@ #include "navigation_mesh_editor_plugin.h" +#ifdef TOOLS_ENABLED +EditorNavigationMeshGenerator *_nav_mesh_generator = NULL; +#endif + void register_recast_types() { +#ifdef TOOLS_ENABLED EditorPlugins::add_by_type(); + _nav_mesh_generator = memnew(EditorNavigationMeshGenerator); + ClassDB::register_class(); + Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", EditorNavigationMeshGenerator::get_singleton())); +#endif } -void unregister_recast_types() {} +void unregister_recast_types() { +#ifdef TOOLS_ENABLED + if (_nav_mesh_generator) { + memdelete(_nav_mesh_generator); + } +#endif +} diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 003f76664de..f82543b789e 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -73,6 +73,41 @@ int NavigationMesh::get_sample_partition_type() const { return static_cast(partition_type); } +void NavigationMesh::set_parsed_geometry_type(int p_value) { + ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX); + parsed_geometry_type = static_cast(p_value); + _change_notify(); +} + +int NavigationMesh::get_parsed_geometry_type() const { + return parsed_geometry_type; +} + +void NavigationMesh::set_collision_mask(uint32_t p_mask) { + + collision_mask = p_mask; +} + +uint32_t NavigationMesh::get_collision_mask() const { + + return collision_mask; +} + +void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool NavigationMesh::get_collision_mask_bit(int p_bit) const { + + return get_collision_mask() & (1 << p_bit); +} + void NavigationMesh::set_cell_size(float p_value) { cell_size = p_value; } @@ -204,6 +239,7 @@ bool NavigationMesh::get_filter_walkable_low_height_spans() const { void NavigationMesh::set_vertices(const PoolVector &p_vertices) { vertices = p_vertices; + _change_notify(); } PoolVector NavigationMesh::get_vertices() const { @@ -217,6 +253,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { for (int i = 0; i < p_array.size(); i++) { polygons.write[i].indices = p_array[i]; } + _change_notify(); } Array NavigationMesh::_get_polygons() const { @@ -235,6 +272,7 @@ void NavigationMesh::add_polygon(const Vector &p_polygon) { Polygon polygon; polygon.indices = p_polygon; polygons.push_back(polygon); + _change_notify(); } int NavigationMesh::get_polygon_count() const { @@ -340,6 +378,15 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationMesh::set_sample_partition_type); ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationMesh::get_sample_partition_type); + ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationMesh::set_parsed_geometry_type); + ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationMesh::get_parsed_geometry_type); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationMesh::set_cell_size); ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationMesh::get_cell_size); @@ -405,10 +452,16 @@ void NavigationMesh::_bind_methods() { BIND_CONSTANT(SAMPLE_PARTITION_MONOTONE); BIND_CONSTANT(SAMPLE_PARTITION_LAYERS); + BIND_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES); + BIND_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS); + BIND_CONSTANT(PARSED_GEOMETRY_BOTH); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type/sample_partition_type", PROPERTY_HINT_ENUM, "Watershed,Monotone,Layers"), "set_sample_partition_type", "get_sample_partition_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry/collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/size", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "cell/height", PROPERTY_HINT_RANGE, "0.1,1.0,0.01,or_greater"), "set_cell_height", "get_cell_height"); @@ -429,6 +482,15 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/filter_walkable_low_height_spans"), "set_filter_walkable_low_height_spans", "get_filter_walkable_low_height_spans"); } +void NavigationMesh::_validate_property(PropertyInfo &property) const { + if (property.name == "geometry/collision_mask") { + if (parsed_geometry_type == PARSED_GEOMETRY_MESH_INSTANCES) { + property.usage = 0; + return; + } + } +} + NavigationMesh::NavigationMesh() { cell_size = 0.3f; cell_height = 0.2f; @@ -445,7 +507,8 @@ NavigationMesh::NavigationMesh() { detail_sample_max_error = 1.0f; partition_type = SAMPLE_PARTITION_WATERSHED; - + parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; + collision_mask = 0xFFFFFFFF; filter_low_hanging_obstacles = false; filter_ledge_spans = false; filter_walkable_low_height_spans = false; @@ -566,8 +629,17 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref &p_na navigation->navmesh_remove(nav_id); nav_id = -1; } + + if (navmesh.is_valid()) { + navmesh->remove_change_receptor(this); + } + navmesh = p_navmesh; + if (navmesh.is_valid()) { + navmesh->add_change_receptor(this); + } + if (navigation && navmesh.is_valid() && enabled) { nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this); } @@ -617,6 +689,11 @@ void NavigationMeshInstance::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); } +void NavigationMeshInstance::_changed_callback(Object *p_changed, const char *p_prop) { + update_gizmo(); + update_configuration_warning(); +} + NavigationMeshInstance::NavigationMeshInstance() { debug_view = NULL; @@ -625,3 +702,8 @@ NavigationMeshInstance::NavigationMeshInstance() { enabled = true; set_notify_transform(true); } + +NavigationMeshInstance::~NavigationMeshInstance() { + if (navmesh.is_valid()) + navmesh->remove_change_receptor(this); +} diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 74531e24236..5fbf3998ffe 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -57,6 +57,7 @@ class NavigationMesh : public Resource { protected: static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; void _set_polygons(const Array &p_array); Array _get_polygons() const; @@ -69,6 +70,13 @@ public: SAMPLE_PARTITION_MAX }; + enum ParsedGeometryType { + PARSED_GEOMETRY_MESH_INSTANCES = 0, + PARSED_GEOMETRY_STATIC_COLLIDERS, + PARSED_GEOMETRY_BOTH, + PARSED_GEOMETRY_MAX + }; + protected: float cell_size; float cell_height; @@ -85,6 +93,8 @@ protected: float detail_sample_max_error; SamplePartitionType partition_type; + ParsedGeometryType parsed_geometry_type; + uint32_t collision_mask; bool filter_low_hanging_obstacles; bool filter_ledge_spans; @@ -95,6 +105,15 @@ public: void set_sample_partition_type(int p_value); int get_sample_partition_type() const; + void set_parsed_geometry_type(int p_value); + int get_parsed_geometry_type() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + void set_cell_size(float p_value); float get_cell_size() const; @@ -174,6 +193,7 @@ class NavigationMeshInstance : public Spatial { protected: void _notification(int p_what); static void _bind_methods(); + void _changed_callback(Object *p_changed, const char *p_prop); public: void set_enabled(bool p_enabled); @@ -185,6 +205,7 @@ public: String get_configuration_warning() const; NavigationMeshInstance(); + ~NavigationMeshInstance(); }; #endif // NAVIGATION_MESH_H