Browse Source

TileSet/TileMap: Decompose solid non-convex polygons into convexes. Real fix for #24003

Mariano Suligoy 6 years ago
parent
commit
078b869d9a

+ 35 - 0
core/math/geometry.cpp

@@ -31,6 +31,7 @@
 #include "geometry.h"
 #include "geometry.h"
 
 
 #include "core/print_string.h"
 #include "core/print_string.h"
+#include "thirdparty/misc/triangulator.h"
 
 
 /* this implementation is very inefficient, commenting unless bugs happen. See the other one.
 /* this implementation is very inefficient, commenting unless bugs happen. See the other one.
 bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
 bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
@@ -737,6 +738,40 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 	return wrapped_faces;
 	return wrapped_faces;
 }
 }
 
 
+Vector<Vector<Vector2> > Geometry::decompose_polygon_in_convex(Vector<Point2> polygon) {
+	Vector<Vector<Vector2> > decomp;
+	List<TriangulatorPoly> in_poly, out_poly;
+
+	TriangulatorPoly inp;
+	inp.Init(polygon.size());
+	for (int i = 0; i < polygon.size(); i++) {
+		inp.GetPoint(i) = polygon[i];
+	}
+	inp.SetOrientation(TRIANGULATOR_CCW);
+	in_poly.push_back(inp);
+	TriangulatorPartition tpart;
+	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
+		ERR_PRINT("Convex decomposing failed!");
+		return decomp;
+	}
+
+	decomp.resize(out_poly.size());
+	int idx = 0;
+	for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {
+		TriangulatorPoly &tp = I->get();
+
+		decomp.write[idx].resize(tp.GetNumPoints());
+
+		for (int i = 0; i < tp.GetNumPoints(); i++) {
+			decomp.write[idx].write[i] = tp.GetPoint(i);
+		}
+
+		idx++;
+	}
+
+	return decomp;
+}
+
 Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes) {
 Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes) {
 
 
 	MeshData mesh;
 	MeshData mesh;

+ 2 - 0
core/math/geometry.h

@@ -950,6 +950,8 @@ public:
 		return H;
 		return H;
 	}
 	}
 
 
+	static Vector<Vector<Vector2> > decompose_polygon_in_convex(Vector<Point2> polygon);
+
 	static MeshData build_convex_mesh(const PoolVector<Plane> &p_planes);
 	static MeshData build_convex_mesh(const PoolVector<Plane> &p_planes);
 	static PoolVector<Plane> build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z);
 	static PoolVector<Plane> build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z);
 	static PoolVector<Plane> build_box_planes(const Vector3 &p_extents);
 	static PoolVector<Plane> build_box_planes(const Vector3 &p_extents);

+ 1 - 34
scene/2d/collision_polygon_2d.cpp

@@ -78,40 +78,7 @@ void CollisionPolygon2D::_build_polygon() {
 }
 }
 
 
 Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
 Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
-
-	Vector<Vector<Vector2> > decomp;
-	List<TriangulatorPoly> in_poly, out_poly;
-
-	TriangulatorPoly inp;
-	inp.Init(polygon.size());
-	for (int i = 0; i < polygon.size(); i++) {
-		inp.GetPoint(i) = polygon[i];
-	}
-	inp.SetOrientation(TRIANGULATOR_CCW);
-	in_poly.push_back(inp);
-	TriangulatorPartition tpart;
-	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
-		ERR_PRINT("Convex decomposing failed!");
-		return decomp;
-	}
-
-	decomp.resize(out_poly.size());
-	int idx = 0;
-
-	for (List<TriangulatorPoly>::Element *I = out_poly.front(); I; I = I->next()) {
-
-		TriangulatorPoly &tp = I->get();
-
-		decomp.write[idx].resize(tp.GetNumPoints());
-
-		for (int i = 0; i < tp.GetNumPoints(); i++) {
-
-			decomp.write[idx].write[i] = tp.GetPoint(i);
-		}
-
-		idx++;
-	}
-
+	Vector<Vector<Vector2> > decomp = Geometry::decompose_polygon_in_convex(polygon);
 	return decomp;
 	return decomp;
 }
 }
 
 

+ 22 - 4
scene/2d/tile_map.cpp

@@ -479,10 +479,28 @@ void TileMap::update_dirty_quadrants() {
 							vs->canvas_item_add_set_transform(debug_canvas_item, xform);
 							vs->canvas_item_add_set_transform(debug_canvas_item, xform);
 							shape->draw(debug_canvas_item, debug_collision_color);
 							shape->draw(debug_canvas_item, debug_collision_color);
 						}
 						}
-						ps->body_add_shape(q.body, shape->get_rid(), xform);
-						ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y));
-						ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
-						shape_idx++;
+
+						if (shape->has_meta("decomposed")) {
+							Array _shapes = shape->get_meta("decomposed");
+							for (int k = 0; k < _shapes.size(); k++) {
+								Ref<ConvexPolygonShape2D> convex = _shapes[k];
+								if (convex.is_valid()) {
+									ps->body_add_shape(q.body, convex->get_rid(), xform);
+									ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y));
+									ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
+									shape_idx++;
+#ifdef DEBUG_ENABLED
+								} else {
+									print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape.");
+#endif
+								}
+							}
+						} else {
+							ps->body_add_shape(q.body, shape->get_rid(), xform);
+							ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y));
+							ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
+							shape_idx++;
+						}
 					}
 					}
 				}
 				}
 			}
 			}

+ 28 - 2
scene/resources/tile_set.cpp

@@ -30,6 +30,7 @@
 
 
 #include "tile_set.h"
 #include "tile_set.h"
 #include "core/array.h"
 #include "core/array.h"
+#include "core/engine.h"
 
 
 bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
 bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
 
 
@@ -662,6 +663,7 @@ void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_sha
 	if (tile_map[p_id].shapes_data.size() <= p_shape_id)
 	if (tile_map[p_id].shapes_data.size() <= p_shape_id)
 		tile_map[p_id].shapes_data.resize(p_shape_id + 1);
 		tile_map[p_id].shapes_data.resize(p_shape_id + 1);
 	tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape;
 	tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape;
+	_decompose_convex_shape(p_shape);
 	emit_changed();
 	emit_changed();
 }
 }
 
 
@@ -844,6 +846,9 @@ void TileSet::tile_set_shapes(int p_id, const Vector<ShapeData> &p_shapes) {
 
 
 	ERR_FAIL_COND(!tile_map.has(p_id));
 	ERR_FAIL_COND(!tile_map.has(p_id));
 	tile_map[p_id].shapes_data = p_shapes;
 	tile_map[p_id].shapes_data = p_shapes;
+	for (int i = 0; i < p_shapes.size(); i++) {
+		_decompose_convex_shape(p_shapes[i].shape);
+	}
 	emit_changed();
 	emit_changed();
 }
 }
 
 
@@ -888,9 +893,10 @@ void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) {
 		} else if (p_shapes[i].get_type() == Variant::DICTIONARY) {
 		} else if (p_shapes[i].get_type() == Variant::DICTIONARY) {
 			Dictionary d = p_shapes[i];
 			Dictionary d = p_shapes[i];
 
 
-			if (d.has("shape") && d["shape"].get_type() == Variant::OBJECT)
+			if (d.has("shape") && d["shape"].get_type() == Variant::OBJECT) {
 				s.shape = d["shape"];
 				s.shape = d["shape"];
-			else
+				_decompose_convex_shape(s.shape);
+			} else
 				continue;
 				continue;
 
 
 			if (d.has("shape_transform") && d["shape_transform"].get_type() == Variant::TRANSFORM2D)
 			if (d.has("shape_transform") && d["shape_transform"].get_type() == Variant::TRANSFORM2D)
@@ -956,6 +962,26 @@ Array TileSet::_get_tiles_ids() const {
 	return arr;
 	return arr;
 }
 }
 
 
+void TileSet::_decompose_convex_shape(Ref<Shape2D> p_shape) {
+	if (Engine::get_singleton()->is_editor_hint())
+		return;
+	Ref<ConvexPolygonShape2D> convex = p_shape;
+	if (!convex.is_valid())
+		return;
+	Vector<Vector<Vector2> > decomp = Geometry::decompose_polygon_in_convex(convex->get_points());
+	if (decomp.size() > 1) {
+		Array sub_shapes;
+		for (int i = 0; i < decomp.size(); i++) {
+			Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
+			_convex->set_points(decomp[i]);
+			sub_shapes.append(_convex);
+		}
+		convex->set_meta("decomposed", sub_shapes);
+	} else {
+		convex->set_meta("decomposed", Variant());
+	}
+}
+
 void TileSet::get_tile_list(List<int> *p_tiles) const {
 void TileSet::get_tile_list(List<int> *p_tiles) const {
 
 
 	for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {
 	for (Map<int, TileData>::Element *E = tile_map.front(); E; E = E->next()) {

+ 2 - 0
scene/resources/tile_set.h

@@ -35,6 +35,7 @@
 #include "core/resource.h"
 #include "core/resource.h"
 #include "scene/2d/light_occluder_2d.h"
 #include "scene/2d/light_occluder_2d.h"
 #include "scene/2d/navigation_polygon.h"
 #include "scene/2d/navigation_polygon.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
 #include "scene/resources/shape_2d.h"
 #include "scene/resources/shape_2d.h"
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 
 
@@ -134,6 +135,7 @@ protected:
 	void _tile_set_shapes(int p_id, const Array &p_shapes);
 	void _tile_set_shapes(int p_id, const Array &p_shapes);
 	Array _tile_get_shapes(int p_id) const;
 	Array _tile_get_shapes(int p_id) const;
 	Array _get_tiles_ids() const;
 	Array _get_tiles_ids() const;
+	void _decompose_convex_shape(Ref<Shape2D> p_shape);
 
 
 	static void _bind_methods();
 	static void _bind_methods();