Merge pull request #32618 from nekomatata/sprite-to-polygon
Sprite to polygon conversion improvements
This commit is contained in:
commit
e2f1b30565
4 changed files with 91 additions and 46 deletions
|
@ -178,6 +178,7 @@ void SpriteEditor::_update_mesh_data() {
|
||||||
err_dialog->popup_centered_minsize();
|
err_dialog->popup_centered_minsize();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Image> image = texture->get_data();
|
Ref<Image> image = texture->get_data();
|
||||||
ERR_FAIL_COND(image.is_null());
|
ERR_FAIL_COND(image.is_null());
|
||||||
Rect2 rect;
|
Rect2 rect;
|
||||||
|
@ -190,7 +191,12 @@ void SpriteEditor::_update_mesh_data() {
|
||||||
bm.instance();
|
bm.instance();
|
||||||
bm->create_from_image_alpha(image);
|
bm->create_from_image_alpha(image);
|
||||||
|
|
||||||
int grow = island_merging->get_value();
|
int shrink = shrink_pixels->get_value();
|
||||||
|
if (shrink > 0) {
|
||||||
|
bm->shrink_mask(shrink, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
int grow = grow_pixels->get_value();
|
||||||
if (grow > 0) {
|
if (grow > 0) {
|
||||||
bm->grow_mask(grow, rect);
|
bm->grow_mask(grow, rect);
|
||||||
}
|
}
|
||||||
|
@ -338,6 +344,13 @@ void SpriteEditor::_convert_to_mesh_2d_node() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteEditor::_convert_to_polygon_2d_node() {
|
void SpriteEditor::_convert_to_polygon_2d_node() {
|
||||||
|
|
||||||
|
if (computed_outline_lines.empty()) {
|
||||||
|
err_dialog->set_text(TTR("Invalid geometry, can't create polygon."));
|
||||||
|
err_dialog->popup_centered_minsize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Polygon2D *polygon_2d_instance = memnew(Polygon2D);
|
Polygon2D *polygon_2d_instance = memnew(Polygon2D);
|
||||||
|
|
||||||
int total_point_count = 0;
|
int total_point_count = 0;
|
||||||
|
@ -362,12 +375,6 @@ void SpriteEditor::_convert_to_polygon_2d_node() {
|
||||||
Vector<Vector2> outline = computed_outline_lines[i];
|
Vector<Vector2> outline = computed_outline_lines[i];
|
||||||
Vector<Vector2> uv_outline = outline_lines[i];
|
Vector<Vector2> uv_outline = outline_lines[i];
|
||||||
|
|
||||||
if (outline.size() < 3) {
|
|
||||||
err_dialog->set_text(TTR("Invalid geometry, can't create polygon."));
|
|
||||||
err_dialog->popup_centered_minsize();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PoolIntArray pia;
|
PoolIntArray pia;
|
||||||
pia.resize(outline.size());
|
pia.resize(outline.size());
|
||||||
PoolIntArray::Write pia_write = pia.write();
|
PoolIntArray::Write pia_write = pia.write();
|
||||||
|
@ -396,16 +403,17 @@ void SpriteEditor::_convert_to_polygon_2d_node() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteEditor::_create_collision_polygon_2d_node() {
|
void SpriteEditor::_create_collision_polygon_2d_node() {
|
||||||
|
|
||||||
|
if (computed_outline_lines.empty()) {
|
||||||
|
err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon."));
|
||||||
|
err_dialog->popup_centered_minsize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < computed_outline_lines.size(); i++) {
|
for (int i = 0; i < computed_outline_lines.size(); i++) {
|
||||||
|
|
||||||
Vector<Vector2> outline = computed_outline_lines[i];
|
Vector<Vector2> outline = computed_outline_lines[i];
|
||||||
|
|
||||||
if (outline.size() < 3) {
|
|
||||||
err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon."));
|
|
||||||
err_dialog->popup_centered_minsize();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);
|
CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D);
|
||||||
collision_polygon_2d_instance->set_polygon(outline);
|
collision_polygon_2d_instance->set_polygon(outline);
|
||||||
|
|
||||||
|
@ -419,16 +427,17 @@ void SpriteEditor::_create_collision_polygon_2d_node() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteEditor::_create_light_occluder_2d_node() {
|
void SpriteEditor::_create_light_occluder_2d_node() {
|
||||||
|
|
||||||
|
if (computed_outline_lines.empty()) {
|
||||||
|
err_dialog->set_text(TTR("Invalid geometry, can't create light occluder."));
|
||||||
|
err_dialog->popup_centered_minsize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < computed_outline_lines.size(); i++) {
|
for (int i = 0; i < computed_outline_lines.size(); i++) {
|
||||||
|
|
||||||
Vector<Vector2> outline = computed_outline_lines[i];
|
Vector<Vector2> outline = computed_outline_lines[i];
|
||||||
|
|
||||||
if (outline.size() < 3) {
|
|
||||||
err_dialog->set_text(TTR("Invalid geometry, can't create light occluder."));
|
|
||||||
err_dialog->popup_centered_minsize();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<OccluderPolygon2D> polygon;
|
Ref<OccluderPolygon2D> polygon;
|
||||||
polygon.instance();
|
polygon.instance();
|
||||||
|
|
||||||
|
@ -531,10 +540,14 @@ void SpriteEditor::_debug_uv_draw() {
|
||||||
|
|
||||||
Ref<Texture> tex = node->get_texture();
|
Ref<Texture> tex = node->get_texture();
|
||||||
ERR_FAIL_COND(!tex.is_valid());
|
ERR_FAIL_COND(!tex.is_valid());
|
||||||
|
|
||||||
|
Point2 draw_pos_offset = Point2(1.0, 1.0);
|
||||||
|
Size2 draw_size_offset = Size2(2.0, 2.0);
|
||||||
|
|
||||||
debug_uv->set_clip_contents(true);
|
debug_uv->set_clip_contents(true);
|
||||||
debug_uv->draw_texture(tex, Point2());
|
debug_uv->draw_texture(tex, draw_pos_offset);
|
||||||
debug_uv->set_custom_minimum_size(tex->get_size());
|
debug_uv->set_custom_minimum_size(tex->get_size() + draw_size_offset);
|
||||||
//debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
|
debug_uv->draw_set_transform(draw_pos_offset, 0, Size2(1.0, 1.0));
|
||||||
|
|
||||||
Color color = Color(1.0, 0.8, 0.7);
|
Color color = Color(1.0, 0.8, 0.7);
|
||||||
|
|
||||||
|
@ -604,13 +617,21 @@ SpriteEditor::SpriteEditor() {
|
||||||
simplification->set_value(2);
|
simplification->set_value(2);
|
||||||
hb->add_child(simplification);
|
hb->add_child(simplification);
|
||||||
hb->add_spacer();
|
hb->add_spacer();
|
||||||
|
hb->add_child(memnew(Label(TTR("Shrink (Pixels): "))));
|
||||||
|
shrink_pixels = memnew(SpinBox);
|
||||||
|
shrink_pixels->set_min(0);
|
||||||
|
shrink_pixels->set_max(10);
|
||||||
|
shrink_pixels->set_step(1);
|
||||||
|
shrink_pixels->set_value(0);
|
||||||
|
hb->add_child(shrink_pixels);
|
||||||
|
hb->add_spacer();
|
||||||
hb->add_child(memnew(Label(TTR("Grow (Pixels): "))));
|
hb->add_child(memnew(Label(TTR("Grow (Pixels): "))));
|
||||||
island_merging = memnew(SpinBox);
|
grow_pixels = memnew(SpinBox);
|
||||||
island_merging->set_min(0);
|
grow_pixels->set_min(0);
|
||||||
island_merging->set_max(10);
|
grow_pixels->set_max(10);
|
||||||
island_merging->set_step(1);
|
grow_pixels->set_step(1);
|
||||||
island_merging->set_value(2);
|
grow_pixels->set_value(2);
|
||||||
hb->add_child(island_merging);
|
hb->add_child(grow_pixels);
|
||||||
hb->add_spacer();
|
hb->add_spacer();
|
||||||
update_preview = memnew(Button);
|
update_preview = memnew(Button);
|
||||||
update_preview->set_text(TTR("Update Preview"));
|
update_preview->set_text(TTR("Update Preview"));
|
||||||
|
|
|
@ -67,7 +67,8 @@ class SpriteEditor : public Control {
|
||||||
Vector<int> computed_indices;
|
Vector<int> computed_indices;
|
||||||
|
|
||||||
SpinBox *simplification;
|
SpinBox *simplification;
|
||||||
SpinBox *island_merging;
|
SpinBox *grow_pixels;
|
||||||
|
SpinBox *shrink_pixels;
|
||||||
Button *update_preview;
|
Button *update_preview;
|
||||||
|
|
||||||
void _menu_option(int p_option);
|
void _menu_option(int p_option);
|
||||||
|
|
|
@ -197,16 +197,14 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)
|
||||||
| 4 | 8 | <- current pixel (curx,cury)
|
| 4 | 8 | <- current pixel (curx,cury)
|
||||||
+---+---+
|
+---+---+
|
||||||
*/
|
*/
|
||||||
//NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel
|
|
||||||
Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2));
|
|
||||||
Point2i tl = Point2i(curx - 1, cury - 1);
|
Point2i tl = Point2i(curx - 1, cury - 1);
|
||||||
sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0;
|
sv += (rect.has_point(tl) && get_bit(tl)) ? 1 : 0;
|
||||||
Point2i tr = Point2i(curx, cury - 1);
|
Point2i tr = Point2i(curx, cury - 1);
|
||||||
sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0;
|
sv += (rect.has_point(tr) && get_bit(tr)) ? 2 : 0;
|
||||||
Point2i bl = Point2i(curx - 1, cury);
|
Point2i bl = Point2i(curx - 1, cury);
|
||||||
sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0;
|
sv += (rect.has_point(bl) && get_bit(bl)) ? 4 : 0;
|
||||||
Point2i br = Point2i(curx, cury);
|
Point2i br = Point2i(curx, cury);
|
||||||
sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0;
|
sv += (rect.has_point(br) && get_bit(br)) ? 8 : 0;
|
||||||
ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>());
|
ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,15 +298,15 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start)
|
||||||
+---+---+
|
+---+---+
|
||||||
| 4 | |
|
| 4 | |
|
||||||
+---+---+
|
+---+---+
|
||||||
this normally go RIGHT, but if its coming from UP, it should go LEFT
|
this normally go RIGHT, but if its coming from RIGHT, it should go LEFT
|
||||||
*/
|
*/
|
||||||
if (case6s.has(Point2i(curx, cury))) {
|
if (case6s.has(Point2i(curx, cury))) {
|
||||||
//found, so we go down, and delete from case6s;
|
//found, so we go left, and delete from case6s;
|
||||||
stepx = -1;
|
stepx = -1;
|
||||||
stepy = 0;
|
stepy = 0;
|
||||||
case6s.erase(Point2i(curx, cury));
|
case6s.erase(Point2i(curx, cury));
|
||||||
} else {
|
} else {
|
||||||
//not found, we go up, and add to case6s;
|
//not found, we go right, and add to case6s;
|
||||||
stepx = 1;
|
stepx = 1;
|
||||||
stepy = 0;
|
stepy = 0;
|
||||||
case6s.insert(Point2i(curx, cury));
|
case6s.insert(Point2i(curx, cury));
|
||||||
|
@ -510,12 +508,19 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl
|
||||||
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
||||||
if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) {
|
if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) {
|
||||||
|
|
||||||
|
fill_bits(this, fill, Point2i(j, i), r);
|
||||||
|
|
||||||
Vector<Vector2> polygon = _march_square(r, Point2i(j, i));
|
Vector<Vector2> polygon = _march_square(r, Point2i(j, i));
|
||||||
print_verbose("BitMap: Pre reduce: " + itos(polygon.size()));
|
print_verbose("BitMap: Pre reduce: " + itos(polygon.size()));
|
||||||
polygon = reduce(polygon, r, p_epsilon);
|
polygon = reduce(polygon, r, p_epsilon);
|
||||||
print_verbose("BitMap: Post reduce: " + itos(polygon.size()));
|
print_verbose("BitMap: Post reduce: " + itos(polygon.size()));
|
||||||
|
|
||||||
|
if (polygon.size() < 3) {
|
||||||
|
print_verbose("Invalid polygon, skipped");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
polygons.push_back(polygon);
|
polygons.push_back(polygon);
|
||||||
fill_bits(this, fill, Point2i(j, i), r);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -525,6 +530,13 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl
|
||||||
|
|
||||||
void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
||||||
|
|
||||||
|
if (p_pixels == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bit_value = (p_pixels > 0) ? true : false;
|
||||||
|
p_pixels = Math::abs(p_pixels);
|
||||||
|
|
||||||
Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
|
Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
|
||||||
|
|
||||||
Ref<BitMap> copy;
|
Ref<BitMap> copy;
|
||||||
|
@ -534,7 +546,7 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
||||||
|
|
||||||
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
|
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
|
||||||
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
||||||
if (copy->get_bit(Point2(j, i)))
|
if (bit_value == get_bit(Point2(j, i)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -542,16 +554,21 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
||||||
for (int y = i - p_pixels; y <= i + p_pixels; y++) {
|
for (int y = i - p_pixels; y <= i + p_pixels; y++) {
|
||||||
for (int x = j - p_pixels; x <= j + p_pixels; x++) {
|
for (int x = j - p_pixels; x <= j + p_pixels; x++) {
|
||||||
|
|
||||||
if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x)
|
bool outside = false;
|
||||||
continue;
|
|
||||||
if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y)
|
if ((x < p_rect.position.x) || (x >= p_rect.position.x + p_rect.size.x) || (y < p_rect.position.y) || (y >= p_rect.position.y + p_rect.size.y)) {
|
||||||
continue;
|
// outside of rectangle counts as bit not set
|
||||||
|
if (!bit_value)
|
||||||
|
outside = true;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON;
|
float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON;
|
||||||
if (d > p_pixels)
|
if (d > p_pixels)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (copy->get_bit(Point2(x, y))) {
|
if (outside || (bit_value == copy->get_bit(Point2(x, y)))) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -561,12 +578,17 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
set_bit(Point2(j, i), true);
|
set_bit(Point2(j, i), bit_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitMap::shrink_mask(int p_pixels, const Rect2 &p_rect) {
|
||||||
|
|
||||||
|
grow_mask(-p_pixels, p_rect);
|
||||||
|
}
|
||||||
|
|
||||||
Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const {
|
Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const {
|
||||||
|
|
||||||
Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon);
|
Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon);
|
||||||
|
|
|
@ -67,6 +67,7 @@ public:
|
||||||
void resize(const Size2 &p_new_size);
|
void resize(const Size2 &p_new_size);
|
||||||
|
|
||||||
void grow_mask(int p_pixels, const Rect2 &p_rect);
|
void grow_mask(int p_pixels, const Rect2 &p_rect);
|
||||||
|
void shrink_mask(int p_pixels, const Rect2 &p_rect);
|
||||||
|
|
||||||
void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap);
|
void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap);
|
||||||
Ref<Image> convert_to_image() const;
|
Ref<Image> convert_to_image() const;
|
||||||
|
|
Loading…
Reference in a new issue