diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml index 9468763b6e3..f9505124ef4 100644 --- a/doc/classes/TileData.xml +++ b/doc/classes/TileData.xml @@ -16,6 +16,13 @@ Adds a collision polygon to the tile on the given TileSet physics layer. + + + + + Adds an occlusion polygon to the tile on the TileSet occlusion layer with index [param layer_id]. + + @@ -78,7 +85,7 @@ [param flip_h], [param flip_v], and [param transpose] allow transforming the returned polygon. - + @@ -89,6 +96,25 @@ [param flip_h], [param flip_v], and [param transpose] allow transforming the returned polygon. + + + + + + + + + Returns the occluder polygon at index [param polygon_index] from the TileSet occlusion layer with index [param layer_id]. + The [param flip_h], [param flip_v], and [param transpose] parameters can be [code]true[/code] to transform the returned polygon. + + + + + + + Returns the number of occluder polygons of the tile in the TileSet occlusion layer with index [param layer_id]. + + @@ -119,6 +145,14 @@ Removes the polygon at index [param polygon_index] for TileSet physics layer with index [param layer_id]. + + + + + + Removes the polygon at index [param polygon_index] for TileSet occlusion layer with index [param layer_id]. + + @@ -194,7 +228,7 @@ Sets the navigation polygon for the TileSet navigation layer with index [param layer_id]. - + @@ -202,6 +236,23 @@ Sets the occluder for the TileSet occlusion layer with index [param layer_id]. + + + + + + + Sets the occluder for polygon with index [param polygon_index] in the TileSet occlusion layer with index [param layer_id]. + + + + + + + + Sets the occluder polygon count in the TileSet occlusion layer with index [param layer_id]. + + diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 8dbf58e2285..79915012a88 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -1482,30 +1482,36 @@ void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Tra debug_occlusion_color.push_back(color); RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - Ref occluder = tile_data->get_occluder(occlusion_layer); - if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { - p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); + for (int i = 0; i < tile_data->get_occluder_polygons_count(occlusion_layer); i++) { + Ref occluder = tile_data->get_occluder_polygon(occlusion_layer, i); + if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { + p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); + } } RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } Variant TileDataOcclusionShapeEditor::_get_painted_value() { - Ref occluder_polygon; - if (polygon_editor->get_polygon_count() >= 1) { + Array polygons; + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + Ref occluder_polygon; occluder_polygon.instantiate(); - occluder_polygon->set_polygon(polygon_editor->get_polygon(0)); + occluder_polygon->set_polygon(polygon_editor->get_polygon(i)); + polygons.push_back(occluder_polygon); } - return occluder_polygon; + return polygons; } void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); ERR_FAIL_NULL(tile_data); - Ref occluder_polygon = tile_data->get_occluder(occlusion_layer); polygon_editor->clear_polygons(); - if (occluder_polygon.is_valid()) { - polygon_editor->add_polygon(occluder_polygon->get_polygon()); + for (int i = 0; i < tile_data->get_occluder_polygons_count(occlusion_layer); i++) { + Ref occluder_polygon = tile_data->get_occluder_polygon(occlusion_layer, i); + if (occluder_polygon.is_valid()) { + polygon_editor->add_polygon(occluder_polygon->get_polygon()); + } } polygon_editor->set_background_tile(p_tile_set_atlas_source, p_coords, p_alternative_tile); } @@ -1513,8 +1519,13 @@ void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, const Variant &p_value) { TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); ERR_FAIL_NULL(tile_data); - Ref occluder_polygon = p_value; - tile_data->set_occluder(occlusion_layer, occluder_polygon); + + Array polygons = p_value; + tile_data->set_occluder_polygons_count(occlusion_layer, polygons.size()); + for (int i = 0; i < polygons.size(); i++) { + Ref occluder_polygon = polygons[i]; + tile_data->set_occluder_polygon(occlusion_layer, i, occluder_polygon); + } polygon_editor->set_background_tile(p_tile_set_atlas_source, p_coords, p_alternative_tile); } @@ -1522,7 +1533,11 @@ void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atl Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); ERR_FAIL_NULL_V(tile_data, Variant()); - return tile_data->get_occluder(occlusion_layer); + Array polygons; + for (int i = 0; i < tile_data->get_occluder_polygons_count(occlusion_layer); i++) { + polygons.push_back(tile_data->get_occluder_polygon(occlusion_layer, i)); + } + return polygons; } void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap &p_previous_values, const Variant &p_new_value) { @@ -1548,6 +1563,7 @@ void TileDataOcclusionShapeEditor::_notification(int p_what) { TileDataOcclusionShapeEditor::TileDataOcclusionShapeEditor() { polygon_editor = memnew(GenericTilePolygonEditor); + polygon_editor->set_multiple_polygon_mode(true); add_child(polygon_editor); } diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 224c4e434f3..b1417b2878f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -2768,15 +2768,7 @@ void EditorPropertyTilePolygon::_add_focusable_children(Node *p_node) { void EditorPropertyTilePolygon::_polygons_changed() { if (String(count_property).is_empty()) { - if (base_type == "OccluderPolygon2D") { - // Single OccluderPolygon2D. - Ref occluder; - if (generic_tile_polygon_editor->get_polygon_count() >= 1) { - occluder.instantiate(); - occluder->set_polygon(generic_tile_polygon_editor->get_polygon(0)); - } - emit_changed(get_edited_property(), occluder); - } else if (base_type == "NavigationPolygon") { + if (base_type == "NavigationPolygon") { Ref navigation_polygon; if (generic_tile_polygon_editor->get_polygon_count() >= 1) { navigation_polygon.instantiate(); @@ -2798,19 +2790,24 @@ void EditorPropertyTilePolygon::_polygons_changed() { emit_changed(get_edited_property(), navigation_polygon); } } else { - if (base_type.is_empty()) { - // Multiple array of vertices. - Vector changed_properties; - Array values; - int count = generic_tile_polygon_editor->get_polygon_count(); - changed_properties.push_back(count_property); - values.push_back(count); - for (int i = 0; i < count; i++) { - changed_properties.push_back(vformat(element_pattern, i)); + // Multiple array of vertices or OccluderPolygon2D. + Vector changed_properties; + Array values; + int count = generic_tile_polygon_editor->get_polygon_count(); + changed_properties.push_back(count_property); + values.push_back(count); + for (int i = 0; i < count; i++) { + changed_properties.push_back(vformat(element_pattern, i)); + if (base_type.is_empty()) { values.push_back(generic_tile_polygon_editor->get_polygon(i)); + } else if (base_type == "OccluderPolygon2D") { + Ref occluder; + occluder.instantiate(); + occluder->set_polygon(generic_tile_polygon_editor->get_polygon(i)); + values.push_back(occluder); } - emit_signal(SNAME("multiple_properties_changed"), changed_properties, values, false); } + emit_signal(SNAME("multiple_properties_changed"), changed_properties, values, false); } } @@ -2834,15 +2831,8 @@ void EditorPropertyTilePolygon::update_property() { generic_tile_polygon_editor->clear_polygons(); if (String(count_property).is_empty()) { - if (base_type == "OccluderPolygon2D") { - // Single OccluderPolygon2D. - Ref occluder = get_edited_property_value(); - generic_tile_polygon_editor->clear_polygons(); - if (occluder.is_valid()) { - generic_tile_polygon_editor->add_polygon(occluder->get_polygon()); - } - } else if (base_type == "NavigationPolygon") { - // Single OccluderPolygon2D. + if (base_type == "NavigationPolygon") { + // Single NavigationPolygon. Ref navigation_polygon = get_edited_property_value(); generic_tile_polygon_editor->clear_polygons(); if (navigation_polygon.is_valid()) { @@ -2859,6 +2849,15 @@ void EditorPropertyTilePolygon::update_property() { for (int i = 0; i < count; i++) { generic_tile_polygon_editor->add_polygon(get_edited_object()->get(vformat(element_pattern, i))); } + } else if (base_type == "OccluderPolygon2D") { + // Multiple OccluderPolygon2D. + generic_tile_polygon_editor->clear_polygons(); + for (int i = 0; i < count; i++) { + Ref occluder = get_edited_object()->get(vformat(element_pattern, i)); + if (occluder.is_valid()) { + generic_tile_polygon_editor->add_polygon(occluder->get_polygon()); + } + } } } } @@ -2899,16 +2898,30 @@ bool EditorInspectorPluginTileData::can_handle(Object *p_object) { bool EditorInspectorPluginTileData::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField p_usage, const bool p_wide) { Vector components = String(p_path).split("/", true, 2); - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + if (components.size() >= 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components[1] == "polygon") { + if (components[1] == "polygons_count") { EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon); - ep->setup_single_mode(p_path, "OccluderPolygon2D"); - add_property_editor(p_path, ep); + ep->setup_multiple_mode(vformat("occlusion_layer_%d/polygons", layer_index), vformat("occlusion_layer_%d/polygons_count", layer_index), vformat("occlusion_layer_%d/polygon_%%d/polygon", layer_index), "OccluderPolygon2D"); + Vector properties; + properties.push_back(p_path); + int count = p_object->get(vformat("occlusion_layer_%d/polygons_count", layer_index)); + for (int i = 0; i < count; i++) { + properties.push_back(vformat("occlusion_layer_%d/polygon_%d/polygon", layer_index, i)); + } + add_property_editor_for_multiple_properties("Polygons", properties, ep); return true; } + // We keep the original editor for now, but here is the code that could be used if we need a custom editor for each polygon: + /*else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (components[2] == "polygon") { + return true; + } + }*/ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp index ba0958e74b7..ebb03e4e739 100644 --- a/scene/2d/tile_map_layer.cpp +++ b/scene/2d/tile_map_layer.cpp @@ -443,13 +443,15 @@ void TileMapLayer::_rendering_notification(int p_what) { Transform2D tilemap_xform = get_global_transform(); for (KeyValue &kv : tile_map_layer_data) { const CellData &cell_data = kv.value; - for (const RID &occluder : cell_data.occluders) { - if (occluder.is_null()) { - continue; + for (const LocalVector &polygons : cell_data.occluders) { + for (const RID &rid : polygons) { + if (rid.is_null()) { + continue; + } + Transform2D xform(0, tile_set->map_to_local(kv.key)); + rs->canvas_light_occluder_attach_to_canvas(rid, get_canvas()); + rs->canvas_light_occluder_set_transform(rid, tilemap_xform * xform); } - Transform2D xform(0, tile_set->map_to_local(kv.key)); - rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas()); - rs->canvas_light_occluder_set_transform(occluder, tilemap_xform * xform); } } } @@ -557,8 +559,10 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) { RenderingServer *rs = RenderingServer::get_singleton(); // Free the occluders. - for (const RID &rid : r_cell_data.occluders) { - rs->free(rid); + for (const LocalVector &polygons : r_cell_data.occluders) { + for (const RID &rid : polygons) { + rs->free(rid); + } } r_cell_data.occluders.clear(); } @@ -566,11 +570,12 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) { void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { RenderingServer *rs = RenderingServer::get_singleton(); - // Free unused occluders then resize the occluders array. + // Free unused occluders then resize the occluder array. for (uint32_t i = tile_set->get_occlusion_layers_count(); i < r_cell_data.occluders.size(); i++) { - RID occluder_id = r_cell_data.occluders[i]; - if (occluder_id.is_valid()) { - rs->free(occluder_id); + for (const RID &occluder_id : r_cell_data.occluders[i]) { + if (occluder_id.is_valid()) { + rs->free(occluder_id); + } } } r_cell_data.occluders.resize(tile_set->get_occlusion_layers_count()); @@ -598,30 +603,42 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { // Create, update or clear occluders. bool needs_set_not_interpolated = is_inside_tree() && get_tree()->is_physics_interpolation_enabled() && !is_physics_interpolated(); for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) { - Ref occluder_polygon = tile_data->get_occluder(occlusion_layer_index); + LocalVector &occluders = r_cell_data.occluders[occlusion_layer_index]; - RID &occluder = r_cell_data.occluders[occlusion_layer_index]; - - if (occluder_polygon.is_valid()) { - // Create or update occluder. - Transform2D xform; - xform.set_origin(tile_set->map_to_local(r_cell_data.coords)); - if (!occluder.is_valid()) { - occluder = rs->canvas_light_occluder_create(); - if (needs_set_not_interpolated) { - rs->canvas_light_occluder_set_interpolated(occluder, false); - } + // Free unused occluders then resize the occluders array. + for (uint32_t i = tile_data->get_occluder_polygons_count(occlusion_layer_index); i < r_cell_data.occluders[occlusion_layer_index].size(); i++) { + RID occluder_id = occluders[i]; + if (occluder_id.is_valid()) { + rs->free(occluder_id); } - rs->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas()); - rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); - rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index)); - } else { - // Clear occluder. - if (occluder.is_valid()) { - rs->free(occluder); - occluder = RID(); + } + occluders.resize(tile_data->get_occluder_polygons_count(occlusion_layer_index)); + + for (uint32_t occlusion_polygon_index = 0; occlusion_polygon_index < occluders.size(); occlusion_polygon_index++) { + RID &occluder = occluders[occlusion_polygon_index]; + Ref occluder_polygon = tile_data->get_occluder_polygon(occlusion_layer_index, occlusion_polygon_index); + if (occluder_polygon.is_valid()) { + // Create or update occluder. + + Transform2D xform; + xform.set_origin(tile_set->map_to_local(r_cell_data.coords)); + if (!occluder.is_valid()) { + occluder = rs->canvas_light_occluder_create(); + if (needs_set_not_interpolated) { + rs->canvas_light_occluder_set_interpolated(occluder, false); + } + } + rs->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder_polygon(occlusion_layer_index, occlusion_polygon_index, flip_h, flip_v, transpose)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); + rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index)); + } else { + // Clear occluder. + if (occluder.is_valid()) { + rs->free(occluder); + occluder = RID(); + } } } } @@ -1709,11 +1726,13 @@ void TileMapLayer::_physics_interpolated_changed() { } for (const KeyValue &E : tile_map_layer_data) { - for (const RID &occluder : E.value.occluders) { - if (occluder.is_valid()) { - rs->canvas_light_occluder_set_interpolated(occluder, interpolated); - if (needs_reset) { - rs->canvas_light_occluder_reset_physics_interpolation(occluder); + for (const LocalVector &polygons : E.value.occluders) { + for (const RID &occluder_id : polygons) { + if (occluder_id.is_valid()) { + rs->canvas_light_occluder_set_interpolated(occluder_id, interpolated); + if (needs_reset) { + rs->canvas_light_occluder_reset_physics_interpolation(occluder_id); + } } } } diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h index 2a986667bd2..cc0a5b49fb9 100644 --- a/scene/2d/tile_map_layer.h +++ b/scene/2d/tile_map_layer.h @@ -108,7 +108,7 @@ struct CellData { // Rendering. Ref rendering_quadrant; SelfList rendering_quadrant_list_element; - LocalVector occluders; + LocalVector> occluders; // Physics. LocalVector bodies; diff --git a/scene/resources/2d/tile_set.compat.inc b/scene/resources/2d/tile_set.compat.inc index 873ae3aa93f..58e33f7b357 100644 --- a/scene/resources/2d/tile_set.compat.inc +++ b/scene/resources/2d/tile_set.compat.inc @@ -37,7 +37,7 @@ Ref TileData::_get_navigation_polygon_bind_compat_84660(int p } Ref TileData::_get_occluder_bind_compat_84660(int p_layer_id) const { - return get_occluder(p_layer_id, false, false, false); + return get_occluder_polygon(p_layer_id, 0, false, false, false); } void TileData::_bind_compatibility_methods() { diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp index dd6ae5096a6..49d84bfb1b9 100644 --- a/scene/resources/2d/tile_set.cpp +++ b/scene/resources/2d/tile_set.cpp @@ -3444,7 +3444,8 @@ void TileSet::_compatibility_conversion() { polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0); } occluder->set_polygon(polygon); - tile_data->set_occluder(0, occluder); + tile_data->add_occluder_polygon(0); + tile_data->set_occluder_polygon(0, 0, occluder); } if (ctd->navigation.is_valid()) { if (get_navigation_layers_count() < 1) { @@ -3558,7 +3559,8 @@ void TileSet::_compatibility_conversion() { polygon.write[index] = xform.xform(polygon[index] - ctd->region.get_size() / 2.0); } occluder->set_polygon(polygon); - tile_data->set_occluder(0, occluder); + tile_data->add_occluder_polygon(0); + tile_data->set_occluder_polygon(0, 0, occluder); } if (ctd->autotile_navpoly_map.has(coords)) { if (get_navigation_layers_count() < 1) { @@ -6220,33 +6222,86 @@ int TileData::get_y_sort_origin() const { return y_sort_origin; } +#ifndef DISABLE_DEPRECATED void TileData::set_occluder(int p_layer_id, Ref p_occluder_polygon) { ERR_FAIL_INDEX(p_layer_id, occluders.size()); - occluders.write[p_layer_id].occluder = p_occluder_polygon; - occluders.write[p_layer_id].transformed_occluders.clear(); + if (get_occluder_polygons_count(p_layer_id) == 0) { + add_occluder_polygon(p_layer_id); + } + set_occluder_polygon(p_layer_id, 0, p_occluder_polygon); emit_signal(CoreStringName(changed)); } Ref TileData::get_occluder(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const { ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref()); + if (get_occluder_polygons_count(p_layer_id) == 0) { + return Ref(); + } + return get_occluder_polygon(p_layer_id, 0, p_flip_h, p_flip_v, p_transpose); +} +#endif // DISABLE_DEPRECATED + +void TileData::set_occluder_polygons_count(int p_layer_id, int p_polygons_count) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + ERR_FAIL_COND(p_polygons_count < 0); + if (p_polygons_count == occluders.write[p_layer_id].polygons.size()) { + return; + } + occluders.write[p_layer_id].polygons.resize(p_polygons_count); + notify_property_list_changed(); + emit_signal(CoreStringName(changed)); +} + +int TileData::get_occluder_polygons_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), 0); + return occluders[p_layer_id].polygons.size(); +} + +void TileData::add_occluder_polygon(int p_layer_id) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + occluders.write[p_layer_id].polygons.push_back(OcclusionLayerTileData::PolygonOccluderTileData()); + emit_signal(CoreStringName(changed)); +} + +void TileData::remove_occluder_polygon(int p_layer_id, int p_polygon_index) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + ERR_FAIL_INDEX(p_polygon_index, occluders[p_layer_id].polygons.size()); + occluders.write[p_layer_id].polygons.remove_at(p_polygon_index); + emit_signal(CoreStringName(changed)); +} + +void TileData::set_occluder_polygon(int p_layer_id, int p_polygon_index, const Ref &p_occluder_polygon) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + ERR_FAIL_INDEX(p_polygon_index, occluders[p_layer_id].polygons.size()); + + OcclusionLayerTileData::PolygonOccluderTileData &polygon_occluder_tile_data = occluders.write[p_layer_id].polygons.write[p_polygon_index]; + polygon_occluder_tile_data.occluder_polygon = p_occluder_polygon; + polygon_occluder_tile_data.transformed_polygon_occluders.clear(); + emit_signal(CoreStringName(changed)); +} + +Ref TileData::get_occluder_polygon(int p_layer_id, int p_polygon_index, bool p_flip_h, bool p_flip_v, bool p_transpose) const { + ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref()); + ERR_FAIL_INDEX_V(p_polygon_index, occluders[p_layer_id].polygons.size(), Ref()); const OcclusionLayerTileData &layer_tile_data = occluders[p_layer_id]; + const Ref &occluder_polygon = layer_tile_data.polygons[p_polygon_index].occluder_polygon; int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2; if (key == 0) { - return layer_tile_data.occluder; + return occluder_polygon; } - if (layer_tile_data.occluder.is_null()) { + if (occluder_polygon.is_null()) { return Ref(); } - HashMap>::Iterator I = layer_tile_data.transformed_occluders.find(key); + HashMap>::Iterator I = layer_tile_data.polygons[p_polygon_index].transformed_polygon_occluders.find(key); if (!I) { Ref transformed_polygon; transformed_polygon.instantiate(); - transformed_polygon->set_polygon(get_transformed_vertices(layer_tile_data.occluder->get_polygon(), p_flip_h, p_flip_v, p_transpose)); - layer_tile_data.transformed_occluders[key] = transformed_polygon; + transformed_polygon->set_polygon(get_transformed_vertices(occluder_polygon->get_polygon(), p_flip_h, p_flip_v, p_transpose)); + layer_tile_data.polygons[p_polygon_index].transformed_polygon_occluders[key] = transformed_polygon; return transformed_polygon; } else { return I->value; @@ -6594,13 +6649,37 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { #endif Vector components = String(p_name).split("/", true, 2); - - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + if (components.size() >= 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components[1] == "polygon") { - Ref polygon = p_value; + if (components.size() == 2) { + if (components[1] == "polygon") { + // Kept for compatibility. + Ref polygon = p_value; + if (layer_index >= occluders.size()) { + if (tile_set) { + return false; + } else { + occluders.resize(layer_index + 1); + } + } + if (get_occluder_polygons_count(layer_index) == 0) { + add_occluder_polygon(layer_index); + } + set_occluder_polygon(layer_index, 0, polygon); + return true; + } else if (components[1] == "polygons_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } + set_occluder_polygons_count(layer_index, p_value); + return true; + } + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + // Polygons. + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); if (layer_index >= occluders.size()) { if (tile_set) { @@ -6609,8 +6688,16 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { occluders.resize(layer_index + 1); } } - set_occluder(layer_index, polygon); - return true; + + if (polygon_index >= occluders[layer_index].polygons.size()) { + occluders.write[layer_index].polygons.resize(polygon_index + 1); + } + + if (components[2] == "polygon") { + Ref polygon = p_value; + set_occluder_polygon(layer_index, polygon_index, polygon); + return true; + } } } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. @@ -6638,6 +6725,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { return true; } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + // Polygons. int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -6724,16 +6812,36 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { Vector components = String(p_name).split("/", true, 2); if (tile_set) { - if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + if (components.size() >= 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { // Occlusion layers. int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); if (layer_index >= occluders.size()) { return false; } - if (components[1] == "polygon") { - r_ret = get_occluder(layer_index); - return true; + if (components.size() == 2) { + if (components[1] == "polygon") { + // Kept for compatibility. + if (occluders[layer_index].polygons.is_empty()) { + return false; + } + r_ret = get_occluder_polygon(layer_index, 0); + return true; + } else if (components[1] == "polygons_count") { + r_ret = get_occluder_polygons_count(layer_index); + return true; + } + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + // Polygons. + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (polygon_index >= occluders[layer_index].polygons.size()) { + return false; + } + if (components[2] == "polygon") { + r_ret = get_occluder_polygon(layer_index, polygon_index); + return true; + } } } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { // Physics layers. @@ -6813,12 +6921,15 @@ void TileData::_get_property_list(List *p_list) const { // Occlusion layers. p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Rendering", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < occluders.size(); i++) { - // occlusion_layer_%d/polygon - property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); - if (occluders[i].occluder.is_null()) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/%s", i, PNAME("polygons_count")), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + for (int j = 0; j < occluders[i].polygons.size(); j++) { + // occlusion_layer_%d/polygon_%d/polygon + property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon_%d/%s", i, j, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); + if (occluders[i].polygons[j].occluder_polygon.is_null()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); } - p_list->push_back(property_info); } // Physics layers. @@ -6923,8 +7034,17 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin); ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin); + ClassDB::bind_method(D_METHOD("set_occluder_polygons_count", "layer_id", "polygons_count"), &TileData::set_occluder_polygons_count); + ClassDB::bind_method(D_METHOD("get_occluder_polygons_count", "layer_id"), &TileData::get_occluder_polygons_count); + ClassDB::bind_method(D_METHOD("add_occluder_polygon", "layer_id"), &TileData::add_occluder_polygon); + ClassDB::bind_method(D_METHOD("remove_occluder_polygon", "layer_id", "polygon_index"), &TileData::remove_occluder_polygon); + ClassDB::bind_method(D_METHOD("set_occluder_polygon", "layer_id", "polygon_index", "polygon"), &TileData::set_occluder_polygon); + ClassDB::bind_method(D_METHOD("get_occluder_polygon", "layer_id", "polygon_index", "flip_h", "flip_v", "transpose"), &TileData::get_occluder_polygon, DEFVAL(false), DEFVAL(false), DEFVAL(false)); + +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder); ClassDB::bind_method(D_METHOD("get_occluder", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_occluder, DEFVAL(false), DEFVAL(false), DEFVAL(false)); +#endif // DISABLE_DEPRECATED // Physics. ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity); diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h index 51df972c8d3..931495d0208 100644 --- a/scene/resources/2d/tile_set.h +++ b/scene/resources/2d/tile_set.h @@ -841,8 +841,11 @@ private: int z_index = 0; int y_sort_origin = 0; struct OcclusionLayerTileData { - Ref occluder; - mutable HashMap> transformed_occluders; + struct PolygonOccluderTileData { + Ref occluder_polygon; + mutable HashMap> transformed_polygon_occluders; + }; + Vector polygons; }; Vector occluders; @@ -941,8 +944,17 @@ public: void set_y_sort_origin(int p_y_sort_origin); int get_y_sort_origin() const; +#ifndef DISABLE_DEPRECATED void set_occluder(int p_layer_id, Ref p_occluder_polygon); Ref get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const; +#endif // DISABLE_DEPRECATED + + void set_occluder_polygons_count(int p_layer_id, int p_polygons_count); + int get_occluder_polygons_count(int p_layer_id) const; + void add_occluder_polygon(int p_layer_id); + void remove_occluder_polygon(int p_layer_id, int p_polygon_index); + void set_occluder_polygon(int p_layer_id, int p_polygon_index, const Ref &p_occluder_polygon); + Ref get_occluder_polygon(int p_layer_id, int p_polygon_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const; // Physics void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);