瀏覽代碼

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.
jfons 6 年之前
父節點
當前提交
1add4c15ab

+ 7 - 5
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("");

+ 154 - 12
modules/recast/navigation_mesh_generator.cpp

@@ -29,14 +29,31 @@
 /*************************************************************************/
 
 #include "navigation_mesh_generator.h"
-
-void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
+#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"
+
+EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::singleton = NULL;
+
+void EditorNavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &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<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void EditorNavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &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<Mesh> &p_mesh, const Transform
 	}
 }
 
-void NavigationMeshGenerator::_parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void EditorNavigationMeshGenerator::_add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+	int face_count = p_faces.size() / 3;
+	int current_vertex_count = p_verticies.size() / 3;
+
+	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<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask) {
 
-	if (Object::cast_to<MeshInstance>(p_node)) {
+	if (Object::cast_to<MeshInstance>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
 
 		MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(p_node);
 		Ref<Mesh> 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<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) {
+
+			for (int i = 0; i < p_node->get_child_count(); ++i) {
+				Node *child = p_node->get_child(i);
+				if (Object::cast_to<CollisionShape>(child)) {
+					CollisionShape *col_shape = Object::cast_to<CollisionShape>(child);
+
+					Transform transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+
+					Ref<Mesh> mesh;
+					Ref<Shape> s = col_shape->get_shape();
+
+					BoxShape *box = Object::cast_to<BoxShape>(*s);
+					if (box) {
+						Ref<CubeMesh> cube_mesh;
+						cube_mesh.instance();
+						cube_mesh->set_size(box->get_extents() * 2.0);
+						mesh = cube_mesh;
+					}
+
+					CapsuleShape *capsule = Object::cast_to<CapsuleShape>(*s);
+					if (capsule) {
+						Ref<CapsuleMesh> 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<CylinderShape>(*s);
+					if (cylinder) {
+						Ref<CylinderMesh> 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<SphereShape>(*s);
+					if (sphere) {
+						Ref<SphereMesh> 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<ConcavePolygonShape>(*s);
+					if (concave_polygon) {
+						_add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices);
+					}
+
+					ConvexPolygonShape *convex_polygon = Object::cast_to<ConvexPolygonShape>(*s);
+					if (convex_polygon) {
+						Vector<Vector3> 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<Spatial>(p_node)) {
+
+		Spatial *spatial = Object::cast_to<Spatial>(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<NavigationMesh> p_nav_mesh) {
+void EditorNavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh) {
 
 	PoolVector<Vector3> nav_vertices;
 
@@ -135,7 +261,7 @@ void NavigationMeshGenerator::_convert_detail_mesh_to_native_navigation_mesh(con
 	}
 }
 
-void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
+void EditorNavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
 		rcHeightfield *hf, rcCompactHeightfield *chf, rcContourSet *cset, rcPolyMesh *poly_mesh, rcPolyMeshDetail *detail_mesh,
 		Vector<float> &vertices, Vector<int> &indices) {
 	rcContext ctx;
@@ -257,7 +383,18 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh>
 	detail_mesh = 0;
 }
 
-void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
+EditorNavigationMeshGenerator *EditorNavigationMeshGenerator::get_singleton() {
+	return singleton;
+}
+
+EditorNavigationMeshGenerator::EditorNavigationMeshGenerator() {
+	singleton = this;
+}
+
+EditorNavigationMeshGenerator::~EditorNavigationMeshGenerator() {
+}
+
+void EditorNavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
 
 	ERR_FAIL_COND(!p_nav_mesh.is_valid());
 
@@ -267,7 +404,7 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
 	Vector<float> vertices;
 	Vector<int> indices;
 
-	_parse_geometry(Object::cast_to<Spatial>(p_node)->get_global_transform().affine_inverse(), p_node, vertices, indices);
+	_parse_geometry(Object::cast_to<Spatial>(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<NavigationMesh> p_nav_mesh, Node *p_node)
 	ep.step(TTR("Done!"), 11);
 }
 
-void NavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
+void EditorNavigationMeshGenerator::clear(Ref<NavigationMesh> p_nav_mesh) {
 	if (p_nav_mesh.is_valid()) {
 		p_nav_mesh->clear_polygons();
 		p_nav_mesh->set_vertices(PoolVector<Vector3>());
 	}
 }
+
+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);
+}

+ 16 - 8
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 <Recast.h>
 
-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<float> &p_verticies);
 	static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
-	static void _parse_geometry(const Transform &p_base_inverse, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices);
+	static void _add_faces(const PoolVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
+	static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &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<NavigationMesh> p_nav_mesh);
 	static void _build_recast_navigation_mesh(Ref<NavigationMesh> p_nav_mesh, EditorProgress *ep,
@@ -52,8 +55,13 @@ protected:
 			rcPolyMeshDetail *detail_mesh, Vector<float> &vertices, Vector<int> &indices);
 
 public:
-	static void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
-	static void clear(Ref<NavigationMesh> p_nav_mesh);
+	static EditorNavigationMeshGenerator *get_singleton();
+
+	EditorNavigationMeshGenerator();
+	~EditorNavigationMeshGenerator();
+
+	void bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node);
+	void clear(Ref<NavigationMesh> p_nav_mesh);
 };
 
 #endif // NAVIGATION_MESH_GENERATOR_H

+ 16 - 1
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<NavigationMeshEditorPlugin>();
+	_nav_mesh_generator = memnew(EditorNavigationMeshGenerator);
+	ClassDB::register_class<EditorNavigationMeshGenerator>();
+	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
+}

+ 83 - 1
scene/3d/navigation_mesh.cpp

@@ -73,6 +73,41 @@ int NavigationMesh::get_sample_partition_type() const {
 	return static_cast<int>(partition_type);
 }
 
+void NavigationMesh::set_parsed_geometry_type(int p_value) {
+	ERR_FAIL_COND(p_value >= PARSED_GEOMETRY_MAX);
+	parsed_geometry_type = static_cast<ParsedGeometryType>(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<Vector3> &p_vertices) {
 
 	vertices = p_vertices;
+	_change_notify();
 }
 
 PoolVector<Vector3> 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<int> &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<NavigationMesh> &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);
+}

+ 21 - 0
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