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

This commit is contained in:
Mariano Suligoy 2019-03-03 23:05:43 -03:00
parent 3aff78f532
commit 078b869d9a
6 changed files with 90 additions and 40 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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 = Geometry::decompose_polygon_in_convex(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; return decomp;
} }

View file

@ -479,6 +479,23 @@ 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);
} }
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_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_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); ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
@ -486,6 +503,7 @@ void TileMap::update_dirty_quadrants() {
} }
} }
} }
}
if (debug_canvas_item.is_valid()) { if (debug_canvas_item.is_valid()) {
vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D()); vs->canvas_item_add_set_transform(debug_canvas_item, Transform2D());

View file

@ -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()) {

View file

@ -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();