Selaa lähdekoodia

Merge pull request #87961 from smix8/navmesh2d_bordersize

Add NavigationPolygon `border_size` property for tile baking
Rémi Verschelde 1 vuosi sitten
vanhempi
commit
8daa633d0d

+ 10 - 0
doc/classes/NavigationPolygon.xml

@@ -174,6 +174,16 @@
 		<member name="agent_radius" type="float" setter="set_agent_radius" getter="get_agent_radius" default="10.0">
 			The distance to erode/shrink the walkable surface when baking the navigation mesh.
 		</member>
+		<member name="baking_rect" type="Rect2" setter="set_baking_rect" getter="get_baking_rect" default="Rect2(0, 0, 0, 0)">
+			If the baking [Rect2] has an area the navigation mesh baking will be restricted to its enclosing area.
+		</member>
+		<member name="baking_rect_offset" type="Vector2" setter="set_baking_rect_offset" getter="get_baking_rect_offset" default="Vector2(0, 0)">
+			The position offset applied to the [member baking_rect] [Rect2].
+		</member>
+		<member name="border_size" type="float" setter="set_border_size" getter="get_border_size" default="0.0">
+			The size of the non-navigable border around the bake bounding area defined by the [member baking_rect] [Rect2].
+			In conjunction with the [member baking_rect] the border size can be used to bake tile aligned navigation meshes without the tile edges being shrunk by [member agent_radius].
+		</member>
 		<member name="cell_size" type="float" setter="set_cell_size" getter="get_cell_size" default="1.0">
 			The cell size used to rasterize the navigation mesh vertices. Must match with the cell size on the navigation map.
 		</member>

+ 38 - 0
modules/navigation/nav_mesh_generator_2d.cpp

@@ -743,9 +743,13 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 	Paths64 traversable_polygon_paths;
 	Paths64 obstruction_polygon_paths;
 
+	traversable_polygon_paths.reserve(outline_count + traversable_outlines.size());
+	obstruction_polygon_paths.reserve(obstruction_outlines.size());
+
 	for (int i = 0; i < outline_count; i++) {
 		const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i);
 		Path64 subject_path;
+		subject_path.reserve(traversable_outline.size());
 		for (const Vector2 &traversable_point : traversable_outline) {
 			const Point64 &point = Point64(traversable_point.x, traversable_point.y);
 			subject_path.push_back(point);
@@ -755,6 +759,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 
 	for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
 		Path64 subject_path;
+		subject_path.reserve(traversable_outline.size());
 		for (const Vector2 &traversable_point : traversable_outline) {
 			const Point64 &point = Point64(traversable_point.x, traversable_point.y);
 			subject_path.push_back(point);
@@ -764,6 +769,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 
 	for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
 		Path64 clip_path;
+		clip_path.reserve(obstruction_outline.size());
 		for (const Vector2 &obstruction_point : obstruction_outline) {
 			const Point64 &point = Point64(obstruction_point.x, obstruction_point.y);
 			clip_path.push_back(point);
@@ -771,6 +777,22 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 		obstruction_polygon_paths.push_back(clip_path);
 	}
 
+	Rect2 baking_rect = p_navigation_mesh->get_baking_rect();
+	if (baking_rect.has_area()) {
+		Vector2 baking_rect_offset = p_navigation_mesh->get_baking_rect_offset();
+
+		const int rect_begin_x = baking_rect.position[0] + baking_rect_offset.x;
+		const int rect_begin_y = baking_rect.position[1] + baking_rect_offset.y;
+		const int rect_end_x = baking_rect.position[0] + baking_rect.size[0] + baking_rect_offset.x;
+		const int rect_end_y = baking_rect.position[1] + baking_rect.size[1] + baking_rect_offset.y;
+
+		Rect64 clipper_rect = Rect64(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y);
+		RectClip rect_clip = RectClip(clipper_rect);
+
+		traversable_polygon_paths = rect_clip.Execute(traversable_polygon_paths);
+		obstruction_polygon_paths = rect_clip.Execute(obstruction_polygon_paths);
+	}
+
 	Paths64 path_solution;
 
 	// first merge all traversable polygons according to user specified fill rule
@@ -787,6 +809,21 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 	}
 	//path_solution = RamerDouglasPeucker(path_solution, 0.025); //
 
+	real_t border_size = p_navigation_mesh->get_border_size();
+	if (baking_rect.has_area() && border_size > 0.0) {
+		Vector2 baking_rect_offset = p_navigation_mesh->get_baking_rect_offset();
+
+		const int rect_begin_x = baking_rect.position[0] + baking_rect_offset.x + border_size;
+		const int rect_begin_y = baking_rect.position[1] + baking_rect_offset.y + border_size;
+		const int rect_end_x = baking_rect.position[0] + baking_rect.size[0] + baking_rect_offset.x - border_size;
+		const int rect_end_y = baking_rect.position[1] + baking_rect.size[1] + baking_rect_offset.y - border_size;
+
+		Rect64 clipper_rect = Rect64(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y);
+		RectClip rect_clip = RectClip(clipper_rect);
+
+		path_solution = rect_clip.Execute(path_solution);
+	}
+
 	Vector<Vector<Vector2>> new_baked_outlines;
 
 	for (const Path64 &scaled_path : path_solution) {
@@ -804,6 +841,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
 	}
 
 	Paths64 polygon_paths;
+	polygon_paths.reserve(new_baked_outlines.size());
 
 	for (const Vector<Vector2> &baked_outline : new_baked_outlines) {
 		Path64 polygon_path;

+ 39 - 0
scene/resources/navigation_polygon.cpp

@@ -347,6 +347,15 @@ real_t NavigationPolygon::get_cell_size() const {
 	return cell_size;
 }
 
+void NavigationPolygon::set_border_size(real_t p_value) {
+	ERR_FAIL_COND(p_value < 0.0);
+	border_size = p_value;
+}
+
+real_t NavigationPolygon::get_border_size() const {
+	return border_size;
+}
+
 void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) {
 	ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX);
 	parsed_geometry_type = p_geometry_type;
@@ -410,6 +419,24 @@ real_t NavigationPolygon::get_agent_radius() const {
 	return agent_radius;
 }
 
+void NavigationPolygon::set_baking_rect(const Rect2 &p_rect) {
+	baking_rect = p_rect;
+	emit_changed();
+}
+
+Rect2 NavigationPolygon::get_baking_rect() const {
+	return baking_rect;
+}
+
+void NavigationPolygon::set_baking_rect_offset(const Vector2 &p_rect_offset) {
+	baking_rect_offset = p_rect_offset;
+	emit_changed();
+}
+
+Vector2 NavigationPolygon::get_baking_rect_offset() const {
+	return baking_rect_offset;
+}
+
 void NavigationPolygon::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices);
 	ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices);
@@ -440,6 +467,9 @@ void NavigationPolygon::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &NavigationPolygon::set_cell_size);
 	ClassDB::bind_method(D_METHOD("get_cell_size"), &NavigationPolygon::get_cell_size);
 
+	ClassDB::bind_method(D_METHOD("set_border_size", "border_size"), &NavigationPolygon::set_border_size);
+	ClassDB::bind_method(D_METHOD("get_border_size"), &NavigationPolygon::get_border_size);
+
 	ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type);
 	ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type);
 
@@ -458,6 +488,11 @@ void NavigationPolygon::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_agent_radius", "agent_radius"), &NavigationPolygon::set_agent_radius);
 	ClassDB::bind_method(D_METHOD("get_agent_radius"), &NavigationPolygon::get_agent_radius);
 
+	ClassDB::bind_method(D_METHOD("set_baking_rect", "rect"), &NavigationPolygon::set_baking_rect);
+	ClassDB::bind_method(D_METHOD("get_baking_rect"), &NavigationPolygon::get_baking_rect);
+	ClassDB::bind_method(D_METHOD("set_baking_rect_offset", "rect_offset"), &NavigationPolygon::set_baking_rect_offset);
+	ClassDB::bind_method(D_METHOD("get_baking_rect_offset"), &NavigationPolygon::get_baking_rect_offset);
+
 	ClassDB::bind_method(D_METHOD("clear"), &NavigationPolygon::clear);
 
 	ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
@@ -473,8 +508,12 @@ void NavigationPolygon::_bind_methods() {
 	ADD_PROPERTY_DEFAULT("source_geometry_group_name", StringName("navigation_polygon_source_geometry_group"));
 	ADD_GROUP("Cells", "");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "1.0,50.0,1.0,or_greater,suffix:px"), "set_cell_size", "get_cell_size");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "border_size", PROPERTY_HINT_RANGE, "0.0,500.0,1.0,or_greater,suffix:px"), "set_border_size", "get_border_size");
 	ADD_GROUP("Agents", "agent_");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "agent_radius", PROPERTY_HINT_RANGE, "0.0,500.0,0.01,or_greater,suffix:px"), "set_agent_radius", "get_agent_radius");
+	ADD_GROUP("Filters", "");
+	ADD_PROPERTY(PropertyInfo(Variant::RECT2, "baking_rect"), "set_baking_rect", "get_baking_rect");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "baking_rect_offset"), "set_baking_rect_offset", "get_baking_rect_offset");
 
 	BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
 	BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);

+ 13 - 0
scene/resources/navigation_polygon.h

@@ -53,6 +53,10 @@ class NavigationPolygon : public Resource {
 	Ref<NavigationMesh> navigation_mesh;
 
 	real_t cell_size = 1.0f; // Must match ProjectSettings default 2D cell_size.
+	real_t border_size = 0.0f;
+
+	Rect2 baking_rect;
+	Vector2 baking_rect_offset;
 
 protected:
 	static void _bind_methods();
@@ -138,6 +142,15 @@ public:
 	void set_cell_size(real_t p_cell_size);
 	real_t get_cell_size() const;
 
+	void set_border_size(real_t p_value);
+	real_t get_border_size() const;
+
+	void set_baking_rect(const Rect2 &p_rect);
+	Rect2 get_baking_rect() const;
+
+	void set_baking_rect_offset(const Vector2 &p_rect_offset);
+	Vector2 get_baking_rect_offset() const;
+
 	void clear();
 
 	NavigationPolygon();