diff options
Diffstat (limited to 'scene')
59 files changed, 2551 insertions, 2078 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 9b4da2a77a..a382fb2f1e 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1322,7 +1322,7 @@ int CharacterBody2D::get_max_slides() const { } void CharacterBody2D::set_max_slides(int p_max_slides) { - ERR_FAIL_COND(p_max_slides > 0); + ERR_FAIL_COND(p_max_slides < 1); max_slides = p_max_slides; } @@ -1396,7 +1396,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction"); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index e9efa1cf84..e2a415e5aa 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,6 +34,8 @@ #include "core/math/geometry_2d.h" #include "core/os/os.h" +#include "servers/navigation_server_2d.h" + void TileMapPattern::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { ERR_FAIL_COND_MSG(p_coords.x < 0 || p_coords.y < 0, vformat("Cannot set cell with negative coords in a TileMapPattern. Wrong coords: %s", p_coords)); @@ -235,40 +237,42 @@ Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffset return output; } -int TileMap::get_effective_quadrant_size() const { +int TileMap::get_effective_quadrant_size(int p_layer) const { // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant - if (tile_set.is_valid() && tile_set->is_y_sorting()) { + if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) { return 1; } else { return quadrant_size; } } -Vector2i TileMap::_coords_to_quadrant_coords(const Vector2i &p_coords) const { - int quadrant_size = get_effective_quadrant_size(); +void TileMap::set_selected_layer(int p_layer_id) { + ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size()); + selected_layer = p_layer_id; + emit_signal(SNAME("changed")); + _make_all_quadrants_dirty(); +} - // Rounding down, instead of simply rounding towards zero (truncating) - return Vector2i( - p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, - p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); +int TileMap::get_selected_layer() const { + return selected_layer; } void TileMap::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { pending_update = true; - _recreate_quadrants(); + _recreate_internals(); } break; case NOTIFICATION_EXIT_TREE: { - _clear_quadrants(); + _clear_internals(); } break; } // Transfers the notification to tileset plugins. if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->tilemap_notification(this, p_what); - } + _rendering_notification(p_what); + _physics_notification(p_what); + _navigation_notification(p_what); } } @@ -283,64 +287,210 @@ void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { // Set the tileset, registering to its changes. if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty)); tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } if (!p_tileset.is_valid()) { - _clear_quadrants(); + _clear_internals(); } tile_set = p_tileset; if (tile_set.is_valid()) { - tile_set->connect("changed", callable_mp(this, &TileMap::_make_all_quadrants_dirty), varray(true)); tile_set->connect("changed", callable_mp(this, &TileMap::_tile_set_changed)); - _recreate_quadrants(); + _recreate_internals(); } emit_signal(SNAME("changed")); } +void TileMap::set_quadrant_size(int p_size) { + ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); + + quadrant_size = p_size; + _recreate_internals(); + emit_signal(SNAME("changed")); +} + int TileMap::get_quadrant_size() const { return quadrant_size; } -void TileMap::set_quadrant_size(int p_size) { - ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); +void TileMap::set_layers_count(int p_layers_count) { + ERR_FAIL_COND(p_layers_count < 0); + _clear_internals(); - quadrant_size = p_size; - _recreate_quadrants(); + layers.resize(p_layers_count); + _recreate_internals(); + notify_property_list_changed(); + + if (selected_layer >= p_layers_count) { + selected_layer = -1; + } + + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +int TileMap::get_layers_count() const { + return layers.size(); +} + +void TileMap::set_layer_name(int p_layer, String p_name) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].name = p_name; + emit_signal(SNAME("changed")); +} + +String TileMap::get_layer_name(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), String()); + return layers[p_layer].name; +} + +void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].enabled = p_enabled; + _recreate_internals(); + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +bool TileMap::is_layer_enabled(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].enabled; +} + +void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].y_sort_enabled = p_y_sort_enabled; + _recreate_internals(); emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +bool TileMap::is_layer_y_sort_enabled(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].y_sort_enabled; +} + +void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].y_sort_origin = p_y_sort_origin; + _recreate_internals(); + emit_signal(SNAME("changed")); +} + +int TileMap::get_layer_y_sort_origin(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].y_sort_origin; +} + +void TileMap::set_layer_z_index(int p_layer, int p_z_index) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + layers[p_layer].z_index = p_z_index; + _recreate_internals(); + emit_signal(SNAME("changed")); + + update_configuration_warnings(); +} + +int TileMap::get_layer_z_index(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); + return layers[p_layer].z_index; } void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { - show_collision = p_show_collision; - _recreate_quadrants(); + collision_visibility_mode = p_show_collision; + _recreate_internals(); emit_signal(SNAME("changed")); } TileMap::VisibilityMode TileMap::get_collision_visibility_mode() { - return show_collision; + return collision_visibility_mode; } void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) { - show_navigation = p_show_navigation; - _recreate_quadrants(); + navigation_visibility_mode = p_show_navigation; + _recreate_internals(); emit_signal(SNAME("changed")); } TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() { - return show_navigation; + return navigation_visibility_mode; } void TileMap::set_y_sort_enabled(bool p_enable) { Node2D::set_y_sort_enabled(p_enable); - _recreate_quadrants(); + _recreate_internals(); emit_signal(SNAME("changed")); } -void TileMap::update_dirty_quadrants() { +Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const { + int quadrant_size = get_effective_quadrant_size(p_layer); + + // Rounding down, instead of simply rounding towards zero (truncating) + return Vector2i( + p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, + p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); +} + +Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + + TileMapQuadrant q; + q.layer = p_layer; + q.coords = p_qk; + + rect_cache_dirty = true; + + // Create the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + q.debug_canvas_item = rs->canvas_item_create(); + rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); + rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + + // Call the create_quadrant method on plugins + if (tile_set.is_valid()) { + _rendering_create_quadrant(&q); + _physics_create_quadrant(&q); + } + + return layers[p_layer].quadrant_map.insert(p_qk, q); +} + +void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) { + // Make the given quadrant dirty, then trigger an update later. + TileMapQuadrant &q = Q->get(); + if (!q.dirty_list_element.in_list()) { + layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element); + } + _queue_update_dirty_quadrants(); +} + +void TileMap::_make_all_quadrants_dirty() { + // Make all quandrants dirty, then trigger an update later. + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + if (!E->value().dirty_list_element.in_list()) { + layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element); + } + } + } + _queue_update_dirty_quadrants(); +} + +void TileMap::_queue_update_dirty_quadrants() { + if (pending_update || !is_inside_tree()) { + return; + } + pending_update = true; + call_deferred(SNAME("_update_dirty_quadrants")); +} + +void TileMap::_update_dirty_quadrants() { if (!pending_update) { return; } @@ -349,43 +499,130 @@ void TileMap::update_dirty_quadrants() { return; } - // Update the coords cache. - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - q->self()->map_to_world.clear(); - q->self()->world_to_map.clear(); - for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { - Vector2i pk = E->get(); - Vector2i pk_world_coords = map_to_world(pk); - q->self()->map_to_world[pk] = pk_world_coords; - q->self()->world_to_map[pk_world_coords] = pk; + for (unsigned int layer = 0; layer < layers.size(); layer++) { + // Update the coords cache. + for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + q->self()->map_to_world.clear(); + q->self()->world_to_map.clear(); + for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { + Vector2i pk = E->get(); + Vector2i pk_world_coords = map_to_world(pk); + q->self()->map_to_world[pk] = pk_world_coords; + q->self()->world_to_map[pk_world_coords] = pk; + } + } + + // Call the update_dirty_quadrant method on plugins. + _rendering_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _physics_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _navigation_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + _scenes_update_dirty_quadrants(layers[layer].dirty_quadrant_list); + + // Redraw the debug canvas_items. + RenderingServer *rs = RenderingServer::get_singleton(); + for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) { + rs->canvas_item_clear(q->self()->debug_canvas_item); + Transform2D xform; + xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer))); + rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); + + _rendering_draw_quadrant_debug(q->self()); + _physics_draw_quadrant_debug(q->self()); + _navigation_draw_quadrant_debug(q->self()); + _scenes_draw_quadrant_debug(q->self()); } - } - // Call the update_dirty_quadrant method on plugins. - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->update_dirty_quadrants(this, dirty_quadrant_list); + // Clear the list + while (layers[layer].dirty_quadrant_list.first()) { + layers[layer].dirty_quadrant_list.remove(layers[layer].dirty_quadrant_list.first()); + } } - // Redraw the debug canvas_items. - RenderingServer *rs = RenderingServer::get_singleton(); - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - rs->canvas_item_clear(q->self()->debug_canvas_item); - Transform2D xform; - xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size())); - rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->draw_quadrant_debug(this, q->self()); + pending_update = false; + + _recompute_rect_cache(); +} + +void TileMap::_recreate_internals() { + // Clear all internals. + _clear_internals(); + + for (unsigned int layer = 0; layer < layers.size(); layer++) { + if (!layers[layer].enabled) { + continue; + } + + // Upadate the layer internals. + _rendering_update_layer(layer); + + // Recreate the quadrants. + const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map; + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y)); + + Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk); + if (!Q) { + Q = _create_quadrant(layer, qk); + layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); + } + + Vector2i pk = E->key(); + Q->get().cells.insert(pk); + + _make_quadrant_dirty(Q); } } - // Clear the list - while (dirty_quadrant_list.first()) { - dirty_quadrant_list.remove(dirty_quadrant_list.first()); + _update_dirty_quadrants(); +} + +void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { + // Remove a quadrant. + TileMapQuadrant *q = &(Q->get()); + + // Call the cleanup_quadrant method on plugins. + if (tile_set.is_valid()) { + _rendering_cleanup_quadrant(q); + _physics_cleanup_quadrant(q); + _navigation_cleanup_quadrant(q); + _scenes_cleanup_quadrant(q); } - pending_update = false; + // Remove the quadrant from the dirty_list if it is there. + if (q->dirty_list_element.in_list()) { + layers[q->layer].dirty_quadrant_list.remove(&(q->dirty_list_element)); + } - _recompute_rect_cache(); + // Free the debug canvas item. + RenderingServer *rs = RenderingServer::get_singleton(); + rs->free(q->debug_canvas_item); + + layers[q->layer].quadrant_map.erase(Q); + rect_cache_dirty = true; +} + +void TileMap::_clear_layer_internals(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + + // Clear quadrants. + while (layers[p_layer].quadrant_map.size()) { + _erase_quadrant(layers[p_layer].quadrant_map.front()); + } + + // Clear the layers internals. + _rendering_cleanup_layer(p_layer); + + // Clear the dirty quadrants list. + while (layers[p_layer].dirty_quadrant_list.first()) { + layers[p_layer].dirty_quadrant_list.remove(layers[p_layer].dirty_quadrant_list.first()); + } +} + +void TileMap::_clear_internals() { + // Clear quadrants. + for (unsigned int layer = 0; layer < layers.size(); layer++) { + _clear_layer_internals(layer); + } } void TileMap::_recompute_rect_cache() { @@ -397,16 +634,18 @@ void TileMap::_recompute_rect_cache() { } Rect2 r_total; - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - Rect2 r; - r.position = map_to_world(E->key() * get_effective_quadrant_size()); - r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size())); - r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size())); - r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size())); - if (E == quadrant_map.front()) { - r_total = r; - } else { - r_total = r_total.merge(r); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + Rect2 r; + r.position = map_to_world(E->key() * get_effective_quadrant_size(layer)); + r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_world((E->key() + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); + r.expand_to(map_to_world((E->key() + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); + if (E == layers[layer].quadrant_map.front()) { + r_total = r; + } else { + r_total = r_total.merge(r); + } } } @@ -418,94 +657,884 @@ void TileMap::_recompute_rect_cache() { #endif } -Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(const Vector2i &p_qk) { - TileMapQuadrant q; - q.coords = p_qk; +/////////////////////////////// Rendering ////////////////////////////////////// - rect_cache_dirty = true; +void TileMap::_rendering_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { + bool visible = is_visible_in_tree(); + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (const RID &occluder : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible); + } + } + } + } + } break; + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (!is_inside_tree()) { + return; + } + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + + // Update occluders transform. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + Transform2D xform; + xform.set_origin(E_cell->key()); + for (const RID &occluder : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); + } + } + } + } + } break; + case CanvasItem::NOTIFICATION_DRAW: { + if (tile_set.is_valid()) { + RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled()); + } + } break; + } +} + +void TileMap::_rendering_update_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); - // Create the debug canvas item. RenderingServer *rs = RenderingServer::get_singleton(); - q.debug_canvas_item = rs->canvas_item_create(); - rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); - rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + if (!layers[p_layer].canvas_item.is_valid()) { + RID ci = rs->canvas_item_create(); + rs->canvas_item_set_parent(ci, get_canvas_item()); - // Call the create_quadrant method on plugins - if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->create_quadrant(this, &q); - } + /*Transform2D xform; + xform.set_origin(Vector2(0, p_layer)); + rs->canvas_item_set_transform(ci, xform);*/ + rs->canvas_item_set_draw_index(ci, p_layer); + + layers[p_layer].canvas_item = ci; } + RID &ci = layers[p_layer].canvas_item; + rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled); + rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + rs->canvas_item_set_z_index(ci, layers[p_layer].z_index); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat())); + rs->canvas_item_set_light_mask(ci, get_light_mask()); +} - return quadrant_map.insert(p_qk, q); +void TileMap::_rendering_cleanup_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + + RenderingServer *rs = RenderingServer::get_singleton(); + if (!layers[p_layer].canvas_item.is_valid()) { + rs->free(layers[p_layer].canvas_item); + } } -void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { - // Remove a quadrant. - TileMapQuadrant *q = &(Q->get()); +void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); - // Call the cleanup_quadrant method on plugins. - if (tile_set.is_valid()) { - for (int i = 0; i < tile_set->get_tile_set_atlas_plugins().size(); i++) { - tile_set->get_tile_set_atlas_plugins()[i]->cleanup_quadrant(this, q); + bool visible = is_visible_in_tree(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + RenderingServer *rs = RenderingServer::get_singleton(); + + // Free the canvas items. + for (const RID &ci : q.canvas_items) { + rs->free(ci); + } + q.canvas_items.clear(); + + // Free the occluders. + for (const RID &occluder : q.occluders) { + rs->free(occluder); } + q.occluders.clear(); + + // Those allow to group cell per material or z-index. + Ref<ShaderMaterial> prev_material; + int prev_z_index = 0; + RID prev_canvas_item; + + // Iterate over the cells of the quadrant. + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(q.layer, E_cell->value(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + Ref<ShaderMaterial> mat = tile_data->tile_get_material(); + int z_index = layers[q.layer].z_index + tile_data->get_z_index(); + + // Quandrant pos. + Vector2 position = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) { + // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. + position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); + } + + // --- CanvasItems --- + // Create two canvas items, for rendering and debug. + RID canvas_item; + + // Check if the material or the z_index changed. + if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { + // If so, create a new CanvasItem. + canvas_item = rs->canvas_item_create(); + if (mat.is_valid()) { + rs->canvas_item_set_material(canvas_item, mat->get_rid()); + } + rs->canvas_item_set_parent(canvas_item, layers[q.layer].canvas_item); + rs->canvas_item_set_use_parent_material(canvas_item, get_use_parent_material() || get_material().is_valid()); + + Transform2D xform; + xform.set_origin(position); + rs->canvas_item_set_transform(canvas_item, xform); + + rs->canvas_item_set_light_mask(canvas_item, get_light_mask()); + rs->canvas_item_set_z_index(canvas_item, z_index); + + rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(get_texture_filter())); + rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(get_texture_repeat())); + + q.canvas_items.push_back(canvas_item); + + prev_canvas_item = canvas_item; + prev_material = mat; + prev_z_index = z_index; + + } else { + // Keep the same canvas_item to draw on. + canvas_item = prev_canvas_item; + } + + // Drawing the tile in the canvas item. + Color modulate = get_self_modulate(); + if (selected_layer >= 0) { + if (q.layer < selected_layer) { + modulate = modulate.darkened(0.5); + } else if (q.layer > selected_layer) { + modulate = modulate.darkened(0.5); + modulate.a *= 0.3; + } + } + draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate); + + // --- Occluders --- + for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { + Transform2D xform; + xform.set_origin(E_cell->key()); + if (tile_data->get_occluder(i).is_valid()) { + RID occluder_id = rs->canvas_light_occluder_create(); + rs->canvas_light_occluder_set_enabled(occluder_id, visible); + rs->canvas_light_occluder_set_transform(occluder_id, get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder_id, get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); + q.occluders.push_back(occluder_id); + } + } + } + } + } + + _rendering_quadrant_order_dirty = true; + q_list_element = q_list_element->next(); } - // Remove the quadrant from the dirty_list if it is there. - if (q->dirty_list_element.in_list()) { - dirty_quadrant_list.remove(&(q->dirty_list_element)); + // Reset the drawing indices + if (_rendering_quadrant_order_dirty) { + int index = -(int64_t)0x80000000; //always must be drawn below children. + + for (int layer = 0; layer < (int)layers.size(); layer++) { + // Sort the quadrants coords per world coordinates + Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + world_to_map[map_to_world(E->key())] = E->key(); + } + + // Sort the quadrants + for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { + TileMapQuadrant &q = layers[layer].quadrant_map[E->value()]; + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_draw_index(ci, index++); + } + } + } + _rendering_quadrant_order_dirty = false; } +} - // Free the debug canvas item. +void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + _rendering_quadrant_order_dirty = true; +} + +void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Free the canvas items. + for (const RID &ci : p_quadrant->canvas_items) { + RenderingServer::get_singleton()->free(ci); + } + p_quadrant->canvas_items.clear(); + + // Free the occluders. + for (const RID &occluder : p_quadrant->occluders) { + RenderingServer::get_singleton()->free(occluder); + } + p_quadrant->occluders.clear(); +} + +void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { + return; + } + + // Draw a placeholder for scenes needing one. RenderingServer *rs = RenderingServer::get_singleton(); - rs->free(q->debug_canvas_item); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); - quadrant_map.erase(Q); - rect_cache_dirty = true; + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.get_atlas_coords()); + to_hash.push_back(c.alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw a placeholder tile. + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } + } } -void TileMap::_make_all_quadrants_dirty(bool p_update) { - // Make all quandrants dirty, then trigger an update later. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - if (!E->value().dirty_list_element.in_list()) { - dirty_quadrant_list.add(&E->value().dirty_list_element); +void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_texture(); + if (!tex.is_valid()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; } + + // Get tile data. + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + + // Compute the offset + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); + Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); + + // Compute the destination rectangle in the CanvasItem. + Rect2 dest_rect; + dest_rect.size = source_rect.size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose(); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h()) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v()) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Get the tile modulation. + Color modulate = tile_data->get_modulate(); + modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); + + // Draw the tile. + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); } +} - if (pending_update) { +/////////////////////////////// Physics ////////////////////////////////////// + +void TileMap::_physics_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + // Update the bodies transforms. + if (is_inside_tree()) { + for (int layer = 0; layer < (int)layers.size(); layer++) { + Transform2D global_transform = get_global_transform(); + + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + + Transform2D xform; + xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer))); + xform = global_transform * xform; + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } break; + } +} + +void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); + + Transform2D global_transform = get_global_transform(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + + // Clear shapes. + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + ps->body_clear_shapes(q.bodies[body_index]); + + // Position the bodies. + Transform2D xform; + xform.set_origin(quadrant_pos); + xform = global_transform * xform; + ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < q.bodies.size(); body_index++) { + // Add the shapes again. + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { + Transform2D xform = Transform2D(); + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + + // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); + ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); + ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); + ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); + } + } + } + } + } + } + + q_list_element = q_list_element->next(); + } +} + +void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + //Get the TileMap's gobla transform. + Transform2D global_transform; + if (is_inside_tree()) { + global_transform = get_global_transform(); + } + + // Clear all bodies. + p_quadrant->bodies.clear(); + + // Create the body and set its parameters. + for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) { + RID body = PhysicsServer2D::get_singleton()->body_create(); + PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); + + PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id()); + PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer)); + PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer)); + + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer); + if (!physics_material.is_valid()) { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + if (is_inside_tree()) { + RID space = get_world_2d()->get_space(); + PhysicsServer2D::get_singleton()->body_set_space(body, space); + + Transform2D xform; + xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(layer))); + xform = global_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + + p_quadrant->bodies.push_back(body); + } +} + +void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Remove a quadrant. + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); + } + p_quadrant->bodies.clear(); +} + +void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!get_tree()) { return; } - pending_update = true; - if (!is_inside_tree()) { + + bool show_collision = false; + switch (collision_visibility_mode) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_collision = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_collision = true; + break; + } + if (!show_collision) { return; } - if (p_update) { - call_deferred(SNAME("update_dirty_quadrants")); + + RenderingServer *rs = RenderingServer::get_singleton(); + + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + + Color debug_collision_color = get_tree()->get_debug_collisions_color(); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); + + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + if (tile_set->has_source(c.source_id)) { + TileSetSource *source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector<Color> color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); + } + } + } + } + } + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); + } +}; + +/////////////////////////////// Navigation ////////////////////////////////////// + +void TileMap::_navigation_notification(int p_what) { + switch (p_what) { + case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { + if (is_inside_tree()) { + for (int layer = 0; layer < (int)layers.size(); layer++) { + Transform2D tilemap_xform = get_global_transform(); + for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { + TileMapQuadrant &q = E_quadrant->get(); + for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { + for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { + RID region = E_region->get()[layer_index]; + if (!region.is_valid()) { + continue; + } + Transform2D tile_transform; + tile_transform.set_origin(map_to_world(E_region->key())); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + } + } + } + } + } + } break; } } -void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update) { - // Make the given quadrant dirty, then trigger an update later. - TileMapQuadrant &q = Q->get(); - if (!q.dirty_list_element.in_list()) { - dirty_quadrant_list.add(&q.dirty_list_element); +void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!is_inside_tree()); + ERR_FAIL_COND(!tile_set.is_valid()); + + // Get colors for debug. + SceneTree *st = SceneTree::get_singleton(); + Color debug_navigation_color; + bool debug_navigation = st && st->is_debugging_navigation_hint(); + if (debug_navigation) { + debug_navigation_color = st->get_debug_navigation_color(); + } + + Transform2D tilemap_xform = get_global_transform(); + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->region_set_map(region, RID()); + } + } + q.navigation_regions.clear(); + + // Get the navigation polygons and create regions. + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly; + navpoly = tile_data->get_navigation_polygon(layer_index); + + if (navpoly.is_valid()) { + Transform2D tile_transform; + tile_transform.set_origin(map_to_world(E_cell->get())); + + RID region = NavigationServer2D::get_singleton()->region_create(); + NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + q.navigation_regions[E_cell->get()].write[layer_index] = region; + } + } + } + } + } + + q_list_element = q_list_element->next(); } +} - if (pending_update) { +void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Clear navigation shapes in the quadrant. + for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { + for (int i = 0; i < E->get().size(); i++) { + RID region = E->get()[i]; + if (!region.is_valid()) { + continue; + } + NavigationServer2D::get_singleton()->free(region); + } + } + p_quadrant->navigation_regions.clear(); +} + +void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + // Draw the debug collision shapes. + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!get_tree()) { return; } - pending_update = true; - if (!is_inside_tree()) { + + bool show_navigation = false; + switch (navigation_visibility_mode) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_navigation = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_navigation = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_navigation = true; + break; + } + if (!show_navigation) { + return; + } + + RenderingServer *rs = RenderingServer::get_singleton(); + + Color color = get_tree()->get_debug_navigation_color(); + RandomPCG rand; + + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); + + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); + if (navpoly.is_valid()) { + PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices(); + + for (int i = 0; i < navpoly->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector<int> polygon = navpoly->get_polygon(i); + Vector<Vector2> vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); + vertices.write[j] = navigation_polygon_vertices[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector<Color> colors; + colors.push_back(random_variation_color); + + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors); + } + } + } + } + } + } +} + +/////////////////////////////// Scenes ////////////////////////////////////// + +void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!tile_set.is_valid()); + + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + + // Clear the scenes. + for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { + Node *node = get_node(E->get()); + if (node) { + node->queue_delete(); + } + } + + q.scenes.clear(); + + // Recreate the scenes. + for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(q.layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); + if (packed_scene.is_valid()) { + Node *scene = packed_scene->instantiate(); + add_child(scene); + Control *scene_as_control = Object::cast_to<Control>(scene); + Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); + if (scene_as_control) { + scene_as_control->set_position(map_to_world(E_cell->get()) + scene_as_control->get_position()); + } else if (scene_as_node2d) { + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get())); + scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); + } + q.scenes[E_cell->get()] = scene->get_name(); + } + } + } + } + + q_list_element = q_list_element->next(); + } +} + +void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { + // Clear the scenes. + for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { + Node *node = get_node(E->get()); + if (node) { + node->queue_delete(); + } + } + + p_quadrant->scenes.clear(); +} + +void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { return; } - if (p_update) { - call_deferred(SNAME("update_dirty_quadrants")); + // Draw a placeholder for scenes needing one. + RenderingServer *rs = RenderingServer::get_singleton(); + Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } + + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw a placeholder tile. + Transform2D xform; + xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } } } -void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { +void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + // Set the current cell tile (using integer position). + Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector2i pk(p_coords); Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk); @@ -526,9 +1555,9 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i } // Get the quadrant - Vector2i qk = _coords_to_quadrant_coords(pk); + Vector2i qk = _coords_to_quadrant_coords(p_layer, pk); - Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); + Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk); if (source_id == TileSet::INVALID_SOURCE) { // Erase existing cell in the tile map. @@ -547,7 +1576,7 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i _make_quadrant_dirty(Q); } - used_size_cache_dirty = true; + used_rect_cache_dirty = true; } else { if (!E) { // Insert a new cell in the tile map. @@ -555,7 +1584,7 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i // Create a new quadrant if needed, then insert the cell if needed. if (!Q) { - Q = _create_quadrant(qk); + Q = _create_quadrant(p_layer, qk); } TileMapQuadrant &q = Q->get(); q.cells.insert(pk); @@ -575,12 +1604,15 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i c.alternative_tile = alternative_tile; _make_quadrant_dirty(Q); - used_size_cache_dirty = true; + used_rect_cache_dirty = true; } } -int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { +int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -595,8 +1627,11 @@ int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) co return E->get().source_id; } -Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { +Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -611,8 +1646,11 @@ Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_pro return E->get().get_atlas_coords(); } -int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { +int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); + // Get a cell source id from position + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { @@ -627,7 +1665,8 @@ int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_prox return E->get().alternative_tile; } -TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) { +TileMapPattern *TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); TileMapPattern *output = memnew(TileMapPattern); @@ -673,7 +1712,7 @@ TileMapPattern *TileMap::get_pattern(TypedArray<Vector2i> p_coords_array) { for (int i = 0; i < coords_in_pattern_array.size(); i++) { Vector2i coords = p_coords_array[i]; Vector2i coords_in_pattern = coords_in_pattern_array[i]; - output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); + output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(p_layer, coords), get_cell_atlas_coords(p_layer, coords), get_cell_alternative_tile(p_layer, coords)); } return output; @@ -702,17 +1741,20 @@ Vector2i TileMap::map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_ return output; } -void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) { +void TileMap::set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(!tile_set.is_valid()); TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern); - set_cell(coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); + set_cell(p_layer, coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); } } -TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { +TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell()); + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; if (!tile_map.has(p_coords)) { return TileMapCell(); } else { @@ -727,77 +1769,62 @@ TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) cons } } -Map<Vector2i, TileMapQuadrant> &TileMap::get_quadrant_map() { - return quadrant_map; +Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + + return &layers[p_layer].quadrant_map; } void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); - Set<Vector2i> coords; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - TileSetSource *source = *tile_set->get_source(E->get().source_id); - if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { - coords.insert(E->key()); + for (unsigned int i = 0; i < layers.size(); i++) { + const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + Set<Vector2i> coords; + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + TileSetSource *source = *tile_set->get_source(E->get().source_id); + if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) { + coords.insert(E->key()); + } } - } - for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { - set_cell(E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - } -} - -void TileMap::_recreate_quadrants() { - // Clear then recreate all quadrants. - _clear_quadrants(); - - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - Vector2i qk = _coords_to_quadrant_coords(Vector2i(E->key().x, E->key().y)); - - Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); - if (!Q) { - Q = _create_quadrant(qk); - dirty_quadrant_list.add(&Q->get().dirty_list_element); + for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { + set_cell(i, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } - - Vector2i pk = E->key(); - Q->get().cells.insert(pk); - - _make_quadrant_dirty(Q, false); } - - update_dirty_quadrants(); } -void TileMap::_clear_quadrants() { - // Clear quadrants. - while (quadrant_map.size()) { - _erase_quadrant(quadrant_map.front()); - } +void TileMap::clear_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); - // Clear the dirty quadrants list. - while (dirty_quadrant_list.first()) { - dirty_quadrant_list.remove(dirty_quadrant_list.first()); - } + // Remove all tiles. + _clear_layer_internals(p_layer); + layers[p_layer].tile_map.clear(); + + used_rect_cache_dirty = true; } void TileMap::clear() { // Remove all tiles. - _clear_quadrants(); - tile_map.clear(); - used_size_cache_dirty = true; + _clear_internals(); + for (unsigned int i = 0; i < layers.size(); i++) { + layers[i].tile_map.clear(); + } + used_rect_cache_dirty = true; } -void TileMap::_set_tile_data(const Vector<int> &p_data) { - // Set data for a given tile from raw data. +void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_COND(format > FORMAT_3); + // Set data for a given tile from raw data. + int c = p_data.size(); const int *r = p_data.ptr(); int offset = (format >= FORMAT_2) ? 3 : 2; ERR_FAIL_COND_MSG(c % offset != 0, "Corrupted tile data."); - clear(); + clear_layer(p_layer); #ifdef DISABLE_DEPRECATED ERR_FAIL_COND_MSG(format != FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", format)); @@ -831,7 +1858,7 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { uint16_t atlas_coords_x = decode_uint16(&local[6]); uint16_t atlas_coords_y = decode_uint16(&local[8]); uint16_t alternative_tile = decode_uint16(&local[10]); - set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + set_cell(p_layer, Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); } else { #ifndef DISABLE_DEPRECATED // Previous decated format. @@ -854,21 +1881,25 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { if (tile_set.is_valid()) { Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); if (a.size() == 3) { - set_cell(Vector2i(x, y), a[0], a[1], a[2]); + set_cell(p_layer, Vector2i(x, y), a[0], a[1], a[2]); } else { ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); } } else { int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); - set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); + set_cell(p_layer, Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); } #endif } } + emit_signal(SNAME("changed")); } -Vector<int> TileMap::_get_tile_data() const { +Vector<int> TileMap::_get_tile_data(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>()); + // Export tile data to raw format + const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector<int> data; data.resize(tile_map.size() * 3); int *w = data.ptrw(); @@ -894,7 +1925,7 @@ Vector<int> TileMap::_get_tile_data() const { Rect2 TileMap::_edit_get_rect() const { // Return the visible rect of the tilemap if (pending_update) { - const_cast<TileMap *>(this)->update_dirty_quadrants(); + const_cast<TileMap *>(this)->_update_dirty_quadrants(); } else { const_cast<TileMap *>(this)->_recompute_rect_cache(); } @@ -903,38 +1934,99 @@ Rect2 TileMap::_edit_get_rect() const { #endif bool TileMap::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { if (p_value.get_type() == Variant::INT) { format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading return true; } - } else if (p_name == "tile_data") { + } else if (p_name == "tile_data") { // Kept for compatibility reasons. if (p_value.is_array()) { - _set_tile_data(p_value); + if (layers.size() < 1) { + layers.resize(1); + } + _set_tile_data(0, p_value); return true; } return false; + } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index < 0 || index >= (int)layers.size()) { + return false; + } + + if (components[1] == "name") { + set_layer_name(index, p_value); + return true; + } else if (components[1] == "enabled") { + set_layer_enabled(index, p_value); + return true; + } else if (components[1] == "y_sort_enabled") { + set_layer_y_sort_enabled(index, p_value); + return true; + } else if (components[1] == "y_sort_origin") { + set_layer_y_sort_origin(index, p_value); + return true; + } else if (components[1] == "z_index") { + set_layer_z_index(index, p_value); + return true; + } else if (components[1] == "tile_data") { + _set_tile_data(index, p_value); + return true; + } else { + return false; + } } return false; } bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { r_ret = FORMAT_3; // When saving, always save highest format return true; - } else if (p_name == "tile_data") { - r_ret = _get_tile_data(); - return true; + } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index < 0 || index >= (int)layers.size()) { + return false; + } + + if (components[1] == "name") { + r_ret = get_layer_name(index); + return true; + } else if (components[1] == "enabled") { + r_ret = is_layer_enabled(index); + return true; + } else if (components[1] == "y_sort_enabled") { + r_ret = is_layer_y_sort_enabled(index); + return true; + } else if (components[1] == "y_sort_origin") { + r_ret = get_layer_y_sort_origin(index); + return true; + } else if (components[1] == "z_index") { + r_ret = get_layer_z_index(index); + return true; + } else if (components[1] == "tile_data") { + r_ret = _get_tile_data(index); + return true; + } else { + return false; + } } return false; } void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyInfo p(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); - p_list->push_back(p); - - p = PropertyInfo(Variant::OBJECT, "tile_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL); - p_list->push_back(p); + p_list->push_back(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::NIL, "Layers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (unsigned int i = 0; i < layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("layer_%d/tile_data", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } } Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { @@ -1557,12 +2649,14 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } } -TypedArray<Vector2i> TileMap::get_used_cells() const { +TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>()); + // Returns the cells used in the tilemap. TypedArray<Vector2i> a; - a.resize(tile_map.size()); + a.resize(layers[p_layer].tile_map.size()); int i = 0; - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) { Vector2i p(E->key().x, E->key().y); a[i++] = p; } @@ -1572,23 +2666,31 @@ TypedArray<Vector2i> TileMap::get_used_cells() const { Rect2 TileMap::get_used_rect() { // Not const because of cache // Return the rect of the currently used area - if (used_size_cache_dirty) { - if (tile_map.size() > 0) { - used_size_cache = Rect2(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0); + if (used_rect_cache_dirty) { + bool first = true; + used_rect_cache = Rect2i(); + + for (unsigned int i = 0; i < layers.size(); i++) { + const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + if (tile_map.size() > 0) { + if (first) { + used_rect_cache = Rect2i(tile_map.front()->key().x, tile_map.front()->key().y, 0, 0); + first = false; + } - for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { - used_size_cache.expand_to(Vector2(E->key().x, E->key().y)); + for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) { + used_rect_cache.expand_to(Vector2i(E->key().x, E->key().y)); + } } - - used_size_cache.size += Vector2(1, 1); - } else { - used_size_cache = Rect2(); } - used_size_cache_dirty = false; + if (!first) { // first is true if every layer is empty. + used_rect_cache.size += Vector2i(1, 1); // The cache expands to top-left coordinate, so we add one full tile. + } + used_rect_cache_dirty = false; } - return used_size_cache; + return used_rect_cache; } // --- Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems --- @@ -1596,10 +2698,13 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache void TileMap::set_light_mask(int p_light_mask) { // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - for (const RID &F : E->get().canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_light_mask(F, get_light_mask()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + for (const RID &ci : E->get().canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask()); + } } + _rendering_update_layer(layer); } } @@ -1608,11 +2713,14 @@ void TileMap::set_material(const Ref<Material> &p_material) { CanvasItem::set_material(p_material); // Update material for the whole tilemap. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + } } + _rendering_update_layer(layer); } } @@ -1621,35 +2729,44 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { CanvasItem::set_use_parent_material(p_use_parent_material); // Update use_parent_material for the whole tilemap. - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(F, get_use_parent_material() || get_material().is_valid()); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + TileMapQuadrant &q = E->get(); + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + } } + _rendering_update_layer(layer); } } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { // Set a default texture filter for the whole tilemap CanvasItem::set_texture_filter(p_texture_filter); - for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - TileMapQuadrant &q = F->get(); - for (const RID &E : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(E, RS::CanvasItemTextureFilter(p_texture_filter)); - _make_quadrant_dirty(F); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(p_texture_filter)); + _make_quadrant_dirty(F); + } } + _rendering_update_layer(layer); } } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { // Set a default texture repeat for the whole tilemap CanvasItem::set_texture_repeat(p_texture_repeat); - for (Map<Vector2i, TileMapQuadrant>::Element *F = quadrant_map.front(); F; F = F->next()) { - TileMapQuadrant &q = F->get(); - for (const RID &E : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(E, RS::CanvasItemTextureRepeat(p_texture_repeat)); - _make_quadrant_dirty(F); + for (unsigned int layer = 0; layer < layers.size(); layer++) { + for (Map<Vector2i, TileMapQuadrant>::Element *F = layers[layer].quadrant_map.front(); F; F = F->next()) { + TileMapQuadrant &q = F->get(); + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(p_texture_repeat)); + _make_quadrant_dirty(F); + } } + _rendering_update_layer(layer); } } @@ -1744,6 +2861,28 @@ void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Colo } } +TypedArray<String> TileMap::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + // Retrieve the set of Z index values with a Y-sorted layer. + Set<int> y_sorted_z_index; + for (int layer = 0; layer < (int)layers.size(); layer++) { + if (layers[layer].y_sort_enabled) { + y_sorted_z_index.insert(layers[layer].z_index); + } + } + + // Check if we have a non-sorted layer in a Z-index with a Y-sorted layer. + for (int layer = 0; layer < (int)layers.size(); layer++) { + if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) { + warnings.push_back(TTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers.")); + break; + } + } + + return warnings; +} + void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset); ClassDB::bind_method(D_METHOD("get_tileset"), &TileMap::get_tileset); @@ -1751,22 +2890,35 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size); ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size); - ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "show_collision"), &TileMap::set_collision_visibility_mode); + ClassDB::bind_method(D_METHOD("set_layers_count", "layers_count"), &TileMap::set_layers_count); + ClassDB::bind_method(D_METHOD("get_layers_count"), &TileMap::get_layers_count); + ClassDB::bind_method(D_METHOD("set_layer_name", "layer", "name"), &TileMap::set_layer_name); + ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); + ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); + ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); + ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); + ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); + ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin); + ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin); + ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index); + ClassDB::bind_method(D_METHOD("get_layer_z_indexd", "layer"), &TileMap::get_layer_z_index); + + ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode); ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode); - ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &TileMap::set_navigation_visibility_mode); + ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode); ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode); - ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); - ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords", "use_proxies"), &TileMap::get_cell_source_id); - ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); - ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); - ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); + ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world); @@ -1774,15 +2926,19 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell); - ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants); + ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants); - ClassDB::bind_method(D_METHOD("_set_tile_data"), &TileMap::_set_tile_data); - ClassDB::bind_method(D_METHOD("_get_tile_data"), &TileMap::_get_tile_data); + ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data); + ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "show_collision", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "show_navigation", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); + + ADD_GROUP("Layers", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers_count"), "set_layers_count", "get_layers_count"); + ADD_PROPERTY_DEFAULT("layers_count", 1); ADD_PROPERTY_DEFAULT("format", FORMAT_1); @@ -1795,17 +2951,19 @@ void TileMap::_bind_methods() { void TileMap::_tile_set_changed() { emit_signal(SNAME("changed")); - _make_all_quadrants_dirty(true); + _recreate_internals(); } TileMap::TileMap() { set_notify_transform(true); set_notify_local_transform(false); + + layers.resize(1); } TileMap::~TileMap() { if (tile_set.is_valid()) { tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); } - _clear_quadrants(); + _clear_internals(); } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 9e35e73ad8..dce18f7682 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -99,7 +99,8 @@ struct TileMapQuadrant { // Dirty list element SelfList<TileMapQuadrant> dirty_list_element; - // Quadrant coords. + // Quadrant layer and coords. + int layer = -1; Vector2i coords; // TileMapCells @@ -126,6 +127,7 @@ struct TileMapQuadrant { Map<Vector2i, String> scenes; void operator=(const TileMapQuadrant &q) { + layer = q.layer; coords = q.coords; debug_canvas_item = q.debug_canvas_item; canvas_items = q.canvas_items; @@ -136,6 +138,7 @@ struct TileMapQuadrant { TileMapQuadrant(const TileMapQuadrant &q) : dirty_list_element(this) { + layer = q.layer; coords = q.coords; debug_canvas_item = q.debug_canvas_item; canvas_items = q.canvas_items; @@ -196,11 +199,13 @@ private: }; mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present; + static constexpr float FP_ADJUST = 0.00001; + // Properties. Ref<TileSet> tile_set; int quadrant_size = 16; - VisibilityMode show_collision = VISIBILITY_MODE_DEFAULT; - VisibilityMode show_navigation = VISIBILITY_MODE_DEFAULT; + VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; + VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT; // Updates. bool pending_update = false; @@ -208,28 +213,72 @@ private: // Rect. Rect2 rect_cache; bool rect_cache_dirty = true; - Rect2 used_size_cache; - bool used_size_cache_dirty = true; + Rect2i used_rect_cache; + bool used_rect_cache_dirty = true; + + // TileMap layers. + struct TileMapLayer { + String name; + bool enabled = true; + bool y_sort_enabled = false; + int y_sort_origin = 0; + int z_index = 0; + RID canvas_item; + Map<Vector2i, TileMapCell> tile_map; + Map<Vector2i, TileMapQuadrant> quadrant_map; + SelfList<TileMapQuadrant>::List dirty_quadrant_list; + }; + LocalVector<TileMapLayer> layers; + int selected_layer = -1; + + // Quadrants and internals management. + Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const; - // Map of cells. - Map<Vector2i, TileMapCell> tile_map; + Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(int p_layer, const Vector2i &p_qk); - // Quadrants management. - Map<Vector2i, TileMapQuadrant> quadrant_map; - Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const; - SelfList<TileMapQuadrant>::List dirty_quadrant_list; + void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q); + void _make_all_quadrants_dirty(); + void _queue_update_dirty_quadrants(); + + void _update_dirty_quadrants(); + + void _recreate_internals(); - Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk); void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q); - void _make_all_quadrants_dirty(bool p_update = true); - void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q, bool p_update = true); - void _recreate_quadrants(); - void _clear_quadrants(); + void _clear_layer_internals(int p_layer); + void _clear_internals(); + + // Rect caching. void _recompute_rect_cache(); + // Per-system methods. + bool _rendering_quadrant_order_dirty = false; + void _rendering_notification(int p_what); + void _rendering_update_layer(int p_layer); + void _rendering_cleanup_layer(int p_layer); + void _rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _rendering_create_quadrant(TileMapQuadrant *p_quadrant); + void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _physics_notification(int p_what); + void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _physics_create_quadrant(TileMapQuadrant *p_quadrant); + void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _navigation_notification(int p_what); + void _navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + + void _scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list); + void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant); + void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant); + // Set and get tiles from data arrays. - void _set_tile_data(const Vector<int> &p_data); - Vector<int> _get_tile_data() const; + void _set_tile_data(int p_layer, const Vector<int> &p_data); + Vector<int> _get_tile_data(int p_layer) const; void _tile_set_changed(); @@ -258,27 +307,45 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); + + // Layers management. + void set_layers_count(int p_layers_count); + int get_layers_count() const; + void set_layer_name(int p_layer, String p_name); + String get_layer_name(int p_layer) const; + void set_layer_enabled(int p_layer, bool p_visible); + bool is_layer_enabled(int p_layer) const; + void set_layer_y_sort_enabled(int p_layer, bool p_enabled); + bool is_layer_y_sort_enabled(int p_layer) const; + void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin); + int get_layer_y_sort_origin(int p_layer) const; + void set_layer_z_index(int p_layer, int p_z_index); + int get_layer_z_index(int p_layer) const; + void set_selected_layer(int p_layer_id); // For editor use. + int get_selected_layer() const; + void set_collision_visibility_mode(VisibilityMode p_show_collision); VisibilityMode get_collision_visibility_mode(); void set_navigation_visibility_mode(VisibilityMode p_show_navigation); VisibilityMode get_navigation_visibility_mode(); - void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE); - int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const; - Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const; - int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const; + void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE); + int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; - TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array); + TileMapPattern *get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array); Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern); - void set_pattern(Vector2i p_position, const TileMapPattern *p_pattern); + void set_pattern(int p_layer, Vector2i p_position, const TileMapPattern *p_pattern); // Not exposed to users - TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const; - Map<Vector2i, TileMapQuadrant> &get_quadrant_map(); - int get_effective_quadrant_size() const; + TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const; + Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer); + int get_effective_quadrant_size(int p_layer) const; + //--- - void update_dirty_quadrants(); virtual void set_y_sort_enabled(bool p_enable) override; Vector2 map_to_world(const Vector2i &p_pos) const; @@ -287,7 +354,7 @@ public: bool is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const; Vector2i get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const; - TypedArray<Vector2i> get_used_cells() const; + TypedArray<Vector2i> get_used_cells(int p_layer) const; Rect2 get_used_rect(); // Not const because of cache // Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems @@ -297,13 +364,19 @@ public: virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override; virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override; + // Fixing a nclearing methods. void fix_invalid_tiles(); + + void clear_layer(int p_layer); void clear(); // Helpers TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords); void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D()); + // Configuration warnings. + TypedArray<String> get_configuration_warnings() const override; + TileMap(); ~TileMap(); }; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 2e962b96c3..e504277a55 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -153,7 +153,7 @@ Transform3D Camera3D::get_camera_transform() const { return tr; } -void Camera3D::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { +void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) { if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) { return; } @@ -168,7 +168,7 @@ void Camera3D::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_f force_change = false; } -void Camera3D::set_orthogonal(float p_size, float p_z_near, float p_z_far) { +void Camera3D::set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) { if (!force_change && size == p_size && p_z_near == near && p_z_far == far && mode == PROJECTION_ORTHOGONAL) { return; } @@ -184,7 +184,7 @@ void Camera3D::set_orthogonal(float p_size, float p_z_near, float p_z_far) { update_gizmos(); } -void Camera3D::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { +void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far) { if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM) { return; } @@ -295,7 +295,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { return get_camera_transform().origin; } else { Vector2 pos = cpos / viewport_size; - float vsize, hsize; + real_t vsize, hsize; if (keep_aspect == KEEP_WIDTH) { vsize = size / viewport_size.aspect(); hsize = size; @@ -368,7 +368,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { return res; } -Vector3 Camera3D::project_position(const Point2 &p_point, float p_z_depth) const { +Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); if (p_z_depth == 0 && mode != PROJECTION_ORTHOGONAL) { @@ -531,15 +531,15 @@ void Camera3D::_bind_methods() { BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP); } -float Camera3D::get_fov() const { +real_t Camera3D::get_fov() const { return fov; } -float Camera3D::get_size() const { +real_t Camera3D::get_size() const { return size; } -float Camera3D::get_near() const { +real_t Camera3D::get_near() const { return near; } @@ -547,7 +547,7 @@ Vector2 Camera3D::get_frustum_offset() const { return frustum_offset; } -float Camera3D::get_far() const { +real_t Camera3D::get_far() const { return far; } @@ -555,19 +555,19 @@ Camera3D::Projection Camera3D::get_projection() const { return mode; } -void Camera3D::set_fov(float p_fov) { +void Camera3D::set_fov(real_t p_fov) { ERR_FAIL_COND(p_fov < 1 || p_fov > 179); fov = p_fov; _update_camera_mode(); } -void Camera3D::set_size(float p_size) { +void Camera3D::set_size(real_t p_size) { ERR_FAIL_COND(p_size < 0.1 || p_size > 16384); size = p_size; _update_camera_mode(); } -void Camera3D::set_near(float p_near) { +void Camera3D::set_near(real_t p_near) { near = p_near; _update_camera_mode(); } @@ -577,7 +577,7 @@ void Camera3D::set_frustum_offset(Vector2 p_offset) { _update_camera_mode(); } -void Camera3D::set_far(float p_far) { +void Camera3D::set_far(real_t p_far) { far = p_far; _update_camera_mode(); } @@ -630,21 +630,21 @@ bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const { return true; } -void Camera3D::set_v_offset(float p_offset) { +void Camera3D::set_v_offset(real_t p_offset) { v_offset = p_offset; _update_camera(); } -float Camera3D::get_v_offset() const { +real_t Camera3D::get_v_offset() const { return v_offset; } -void Camera3D::set_h_offset(float p_offset) { +void Camera3D::set_h_offset(real_t p_offset) { h_offset = p_offset; _update_camera(); } -float Camera3D::get_h_offset() const { +real_t Camera3D::get_h_offset() const { return h_offset; } @@ -672,11 +672,11 @@ Camera3D::~Camera3D() { //////////////////////////////////////// -void ClippedCamera3D::set_margin(float p_margin) { +void ClippedCamera3D::set_margin(real_t p_margin) { margin = p_margin; } -float ClippedCamera3D::get_margin() const { +real_t ClippedCamera3D::get_margin() const { return margin; } @@ -746,7 +746,7 @@ void ClippedCamera3D::_notification(int p_what) { xf.origin = ray_from; xf.orthonormalize(); - float closest_safe = 1.0f, closest_unsafe = 1.0f; + real_t closest_safe = 1.0f, closest_unsafe = 1.0f; if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) { clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe); } @@ -813,7 +813,7 @@ void ClippedCamera3D::clear_exceptions() { exclude.clear(); } -float ClippedCamera3D::get_clip_offset() const { +real_t ClippedCamera3D::get_clip_offset() const { return clip_offset; } diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index c6efc8f9a9..61e3f51d0b 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -63,13 +63,13 @@ private: Projection mode = PROJECTION_PERSPECTIVE; - float fov = 0.0; - float size = 1.0; + real_t fov = 0.0; + real_t size = 1.0; Vector2 frustum_offset; - float near = 0.0; - float far = 0.0; - float v_offset = 0.0; - float h_offset = 0.0; + real_t near = 0.0; + real_t far = 0.0; + real_t v_offset = 0.0; + real_t h_offset = 0.0; KeepAspect keep_aspect = KEEP_HEIGHT; RID camera; @@ -107,10 +107,9 @@ public: NOTIFICATION_LOST_CURRENT = 51 }; - void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); - void set_orthogonal(float p_size, float p_z_near, float p_z_far); - void set_frustum(float p_size, Vector2 p_offset, float p_z_near, - float p_z_far); + void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); + void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); + void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far); void set_projection(Camera3D::Projection p_mode); void make_current(); @@ -120,18 +119,18 @@ public: RID get_camera() const; - float get_fov() const; - float get_size() const; - float get_far() const; - float get_near() const; + real_t get_fov() const; + real_t get_size() const; + real_t get_far() const; + real_t get_near() const; Vector2 get_frustum_offset() const; Projection get_projection() const; - void set_fov(float p_fov); - void set_size(float p_size); - void set_far(float p_far); - void set_near(float p_near); + void set_fov(real_t p_fov); + void set_size(real_t p_size); + void set_far(real_t p_far); + void set_near(real_t p_near); void set_frustum_offset(Vector2 p_offset); virtual Transform3D get_camera_transform() const; @@ -141,8 +140,7 @@ public: virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; bool is_position_behind(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point, - float p_z_depth) const; + virtual Vector3 project_position(const Point2 &p_point, real_t p_z_depth) const; Vector<Vector3> get_near_plane_points() const; @@ -164,11 +162,11 @@ public: void set_keep_aspect_mode(KeepAspect p_aspect); KeepAspect get_keep_aspect_mode() const; - void set_v_offset(float p_offset); - float get_v_offset() const; + void set_v_offset(real_t p_offset); + real_t get_v_offset() const; - void set_h_offset(float p_offset); - float get_h_offset() const; + void set_h_offset(real_t p_offset); + real_t get_h_offset() const; void set_doppler_tracking(DopplerTracking p_tracking); DopplerTracking get_doppler_tracking() const; @@ -195,8 +193,8 @@ public: private: ClipProcessCallback process_callback = CLIP_PROCESS_PHYSICS; RID pyramid_shape; - float margin = 0.0; - float clip_offset = 0.0; + real_t margin = 0.0; + real_t clip_offset = 0.0; uint32_t collision_mask = 1; bool clip_to_areas = false; bool clip_to_bodies = true; @@ -217,8 +215,8 @@ public: void set_clip_to_bodies(bool p_clip); bool is_clip_to_bodies_enabled() const; - void set_margin(float p_margin); - float get_margin() const; + void set_margin(real_t p_margin); + real_t get_margin() const; void set_process_callback(ClipProcessCallback p_mode); ClipProcessCallback get_process_callback() const; @@ -235,7 +233,7 @@ public: void remove_exception(const Object *p_object); void clear_exceptions(); - float get_clip_offset() const; + real_t get_clip_offset() const; ClippedCamera3D(); ~ClippedCamera3D(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 60f8ad8f36..56e013a6a0 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -91,11 +91,11 @@ void CPUParticles3D::set_pre_process_time(float p_time) { pre_process_time = p_time; } -void CPUParticles3D::set_explosiveness_ratio(float p_ratio) { +void CPUParticles3D::set_explosiveness_ratio(real_t p_ratio) { explosiveness_ratio = p_ratio; } -void CPUParticles3D::set_randomness_ratio(float p_ratio) { +void CPUParticles3D::set_randomness_ratio(real_t p_ratio) { randomness_ratio = p_ratio; } @@ -107,7 +107,7 @@ void CPUParticles3D::set_use_local_coordinates(bool p_enable) { local_coords = p_enable; } -void CPUParticles3D::set_speed_scale(float p_scale) { +void CPUParticles3D::set_speed_scale(real_t p_scale) { speed_scale = p_scale; } @@ -131,11 +131,11 @@ float CPUParticles3D::get_pre_process_time() const { return pre_process_time; } -float CPUParticles3D::get_explosiveness_ratio() const { +real_t CPUParticles3D::get_explosiveness_ratio() const { return explosiveness_ratio; } -float CPUParticles3D::get_randomness_ratio() const { +real_t CPUParticles3D::get_randomness_ratio() const { return randomness_ratio; } @@ -147,7 +147,7 @@ bool CPUParticles3D::get_use_local_coordinates() const { return local_coords; } -float CPUParticles3D::get_speed_scale() const { +real_t CPUParticles3D::get_speed_scale() const { return speed_scale; } @@ -247,47 +247,47 @@ Vector3 CPUParticles3D::get_direction() const { return direction; } -void CPUParticles3D::set_spread(float p_spread) { +void CPUParticles3D::set_spread(real_t p_spread) { spread = p_spread; } -float CPUParticles3D::get_spread() const { +real_t CPUParticles3D::get_spread() const { return spread; } -void CPUParticles3D::set_flatness(float p_flatness) { +void CPUParticles3D::set_flatness(real_t p_flatness) { flatness = p_flatness; } -float CPUParticles3D::get_flatness() const { +real_t CPUParticles3D::get_flatness() const { return flatness; } -void CPUParticles3D::set_param(Parameter p_param, float p_value) { +void CPUParticles3D::set_param(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); parameters[p_param] = p_value; } -float CPUParticles3D::get_param(Parameter p_param) const { +real_t CPUParticles3D::get_param(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return parameters[p_param]; } -void CPUParticles3D::set_param_randomness(Parameter p_param, float p_value) { +void CPUParticles3D::set_param_randomness(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); randomness[p_param] = p_value; } -float CPUParticles3D::get_param_randomness(Parameter p_param) const { +real_t CPUParticles3D::get_param_randomness(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return randomness[p_param]; } -static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) { +static void _adjust_curve_range(const Ref<Curve> &p_curve, real_t p_min, real_t p_max) { Ref<Curve> curve = p_curve; if (!curve.is_valid()) { return; @@ -381,7 +381,7 @@ void CPUParticles3D::set_emission_shape(EmissionShape p_shape) { emission_shape = p_shape; } -void CPUParticles3D::set_emission_sphere_radius(float p_radius) { +void CPUParticles3D::set_emission_sphere_radius(real_t p_radius) { emission_sphere_radius = p_radius; } @@ -405,19 +405,19 @@ void CPUParticles3D::set_emission_ring_axis(Vector3 p_axis) { emission_ring_axis = p_axis; } -void CPUParticles3D::set_emission_ring_height(float p_height) { +void CPUParticles3D::set_emission_ring_height(real_t p_height) { emission_ring_height = p_height; } -void CPUParticles3D::set_emission_ring_radius(float p_radius) { +void CPUParticles3D::set_emission_ring_radius(real_t p_radius) { emission_ring_radius = p_radius; } -void CPUParticles3D::set_emission_ring_inner_radius(float p_radius) { +void CPUParticles3D::set_emission_ring_inner_radius(real_t p_radius) { emission_ring_inner_radius = p_radius; } -float CPUParticles3D::get_emission_sphere_radius() const { +real_t CPUParticles3D::get_emission_sphere_radius() const { return emission_sphere_radius; } @@ -441,15 +441,15 @@ Vector3 CPUParticles3D::get_emission_ring_axis() const { return emission_ring_axis; } -float CPUParticles3D::get_emission_ring_height() const { +real_t CPUParticles3D::get_emission_ring_height() const { return emission_ring_height; } -float CPUParticles3D::get_emission_ring_radius() const { +real_t CPUParticles3D::get_emission_ring_radius() const { return emission_ring_radius; } -float CPUParticles3D::get_emission_ring_inner_radius() const { +real_t CPUParticles3D::get_emission_ring_inner_radius() const { return emission_ring_inner_radius; } @@ -498,7 +498,7 @@ static uint32_t idhash(uint32_t x) { return x; } -static float rand_from_seed(uint32_t &seed) { +static real_t rand_from_seed(uint32_t &seed) { int k; int s = int(seed); if (s == 0) { @@ -510,7 +510,7 @@ static float rand_from_seed(uint32_t &seed) { s += 2147483647; } seed = uint32_t(s); - return float(seed % uint32_t(65536)) / 65535.0; + return (seed % uint32_t(65536)) / 65535.0; } void CPUParticles3D::_update_internal() { @@ -627,7 +627,7 @@ void CPUParticles3D::_particles_process(float p_delta) { // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle. // While we use time in tests later on, for randomness we use the phase as done in the // original shader code, and we later multiply by lifetime to get the time. - float restart_phase = float(i) / float(pcount); + real_t restart_phase = real_t(i) / real_t(pcount); if (randomness_ratio > 0.0) { uint32_t seed = cycle; @@ -636,8 +636,8 @@ void CPUParticles3D::_particles_process(float p_delta) { } seed *= uint32_t(pcount); seed += uint32_t(i); - float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_phase += randomness_ratio * random * 1.0 / float(pcount); + real_t random = (idhash(seed) % uint32_t(65536)) / real_t(65536.0); + restart_phase += randomness_ratio * random * 1.0 / pcount; } restart_phase *= (1.0 - explosiveness_ratio); @@ -682,17 +682,17 @@ void CPUParticles3D::_particles_process(float p_delta) { } p.active = true; - /*float tex_linear_velocity = 0; + /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); }*/ - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); } @@ -705,26 +705,26 @@ void CPUParticles3D::_particles_process(float p_delta) { p.anim_offset_rand = Math::randf(); if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - float angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); - p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); } else { //initiate velocity spread in 3D - float angle1_rad = Math::atan2(direction.x, direction.z) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); - float angle2_rad = Math::atan2(direction.y, Math::abs(direction.z)) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * spread); + real_t angle1_rad = Math::atan2(direction.x, direction.z) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle2_rad = Math::atan2(direction.y, Math::abs(direction.z)) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * spread); Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); direction_yz.z = direction_yz.z / MAX(0.0001, Math::sqrt(ABS(direction_yz.z))); //better uniform distribution Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); direction.normalize(); - p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp((real_t)1.0, real_t(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); } - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); p.custom[0] = Math::deg2rad(base_angle); //angle p.custom[1] = 0.0; //phase - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1) + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1) p.transform = Transform3D(); p.time = 0; p.lifetime = lifetime * (1.0 - Math::randf() * lifetime_randomness); @@ -783,8 +783,8 @@ void CPUParticles3D::_particles_process(float p_delta) { } } break; case EMISSION_SHAPE_RING: { - float ring_random_angle = Math::randf() * 2.0 * Math_PI; - float ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; + real_t ring_random_angle = Math::randf() * Math_TAU; + real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; Vector3 axis = emission_ring_axis.normalized(); Vector3 ortho_axis = Vector3(); if (axis == Vector3(1.0, 0.0, 0.0)) { @@ -824,53 +824,53 @@ void CPUParticles3D::_particles_process(float p_delta) { p.custom[1] = p.time / lifetime; tv = p.time / p.lifetime; - float tex_linear_velocity = 0.0; + real_t tex_linear_velocity = 0.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); } - float tex_orbit_velocity = 0.0; + real_t tex_orbit_velocity = 0.0; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); } } - float tex_angular_velocity = 0.0; + real_t tex_angular_velocity = 0.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); } - float tex_linear_accel = 0.0; + real_t tex_linear_accel = 0.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); } - float tex_tangential_accel = 0.0; + real_t tex_tangential_accel = 0.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); } - float tex_radial_accel = 0.0; + real_t tex_radial_accel = 0.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); } - float tex_damping = 0.0; + real_t tex_damping = 0.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); } - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_speed = 0.0; + real_t tex_anim_speed = 0.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); } @@ -881,28 +881,28 @@ void CPUParticles3D::_particles_process(float p_delta) { position.z = 0.0; } //apply linear acceleration - force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3(); + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3(); //apply radial acceleration Vector3 org = emission_xform.origin; Vector3 diff = position - org; - force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); + force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); //apply tangential acceleration; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector2 yx = Vector2(diff.y, diff.x); Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); - force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); } else { Vector3 crossDiff = diff.normalized().cross(gravity.normalized()); - force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); } //apply attractor forces p.velocity += force * local_delta; //orbit velocity if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { - float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); + real_t orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); if (orbit_amount != 0.0) { - float ang = orbit_amount * local_delta * Math_TAU; + real_t ang = orbit_amount * local_delta * Math_TAU; // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, // but we use -ang here to reproduce its behavior. Transform2D rot = Transform2D(-ang, Vector2()); @@ -915,8 +915,8 @@ void CPUParticles3D::_particles_process(float p_delta) { p.velocity = p.velocity.normalized() * tex_linear_velocity; } if (parameters[PARAM_DAMPING] + tex_damping > 0.0) { - float v = p.velocity.length(); - float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + real_t v = p.velocity.length(); + real_t damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); v -= damp * local_delta; if (v < 0.0) { p.velocity = Vector3(); @@ -924,27 +924,27 @@ void CPUParticles3D::_particles_process(float p_delta) { p.velocity = p.velocity.normalized() * v; } } - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); - base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + real_t base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp((real_t)1.0, p.angle_rand, randomness[PARAM_ANGLE]); + base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); p.custom[0] = Math::deg2rad(base_angle); //angle - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle + p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp((real_t)1.0, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp((real_t)1.0, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle } //apply color //apply hue rotation - float tex_scale = 1.0; + real_t tex_scale = 1.0; if (curve_parameters[PARAM_SCALE].is_valid()) { tex_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); } - float tex_hue_variation = 0.0; + real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); } - float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_TAU * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); - float hue_rot_c = Math::cos(hue_rot_angle); - float hue_rot_s = Math::sin(hue_rot_angle); + real_t hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_TAU * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); + real_t hue_rot_c = Math::cos(hue_rot_angle); + real_t hue_rot_s = Math::sin(hue_rot_angle); Basis hue_rot_mat; { @@ -1013,9 +1013,9 @@ void CPUParticles3D::_particles_process(float p_delta) { } //scale by scale - float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]); - if (base_scale < 0.000001) { - base_scale = 0.000001; + real_t base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], (real_t)1.0, p.scale_rand * randomness[PARAM_SCALE]); + if (base_scale < CMP_EPSILON) { + base_scale = CMP_EPSILON; } p.transform.basis.scale(Vector3(1, 1, 1) * base_scale); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 07d345ba2c..f8d0d79014 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -89,10 +89,10 @@ private: float custom[4] = {}; Vector3 velocity; bool active = false; - float angle_rand = 0.0; - float scale_rand = 0.0; - float hue_rot_rand = 0.0; - float anim_offset_rand = 0.0; + real_t angle_rand = 0.0; + real_t scale_rand = 0.0; + real_t hue_rot_rand = 0.0; + real_t anim_offset_rand = 0.0; float time = 0.0; float lifetime = 0.0; Color base_color; @@ -134,8 +134,8 @@ private: float lifetime = 1.0; float pre_process_time = 0.0; - float explosiveness_ratio = 0.0; - float randomness_ratio = 0.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; float lifetime_randomness = 0.0; float speed_scale = 1.0; bool local_coords = true; @@ -153,11 +153,11 @@ private: //////// Vector3 direction = Vector3(1, 0, 0); - float spread = 45.0; - float flatness = 0.0; + real_t spread = 45.0; + real_t flatness = 0.0; - float parameters[PARAM_MAX]; - float randomness[PARAM_MAX] = {}; + real_t parameters[PARAM_MAX]; + real_t randomness[PARAM_MAX] = {}; Ref<Curve> curve_parameters[PARAM_MAX]; Color color = Color(1, 1, 1, 1); @@ -166,16 +166,16 @@ private: bool particle_flags[PARTICLE_FLAG_MAX] = {}; EmissionShape emission_shape = EMISSION_SHAPE_POINT; - float emission_sphere_radius = 1.0; + real_t emission_sphere_radius = 1.0; Vector3 emission_box_extents = Vector3(1, 1, 1); Vector<Vector3> emission_points; Vector<Vector3> emission_normals; Vector<Color> emission_colors; int emission_point_count = 0; Vector3 emission_ring_axis; - float emission_ring_height; - float emission_ring_radius; - float emission_ring_inner_radius; + real_t emission_ring_height; + real_t emission_ring_radius; + real_t emission_ring_inner_radius; Vector3 gravity = Vector3(0, -9.8, 0); @@ -203,24 +203,24 @@ public: void set_lifetime(float p_lifetime); void set_one_shot(bool p_one_shot); void set_pre_process_time(float p_time); - void set_explosiveness_ratio(float p_ratio); - void set_randomness_ratio(float p_ratio); + void set_explosiveness_ratio(real_t p_ratio); + void set_randomness_ratio(real_t p_ratio); void set_lifetime_randomness(float p_random); void set_visibility_aabb(const AABB &p_aabb); void set_use_local_coordinates(bool p_enable); - void set_speed_scale(float p_scale); + void set_speed_scale(real_t p_scale); bool is_emitting() const; int get_amount() const; float get_lifetime() const; bool get_one_shot() const; float get_pre_process_time() const; - float get_explosiveness_ratio() const; - float get_randomness_ratio() const; + real_t get_explosiveness_ratio() const; + real_t get_randomness_ratio() const; float get_lifetime_randomness() const; AABB get_visibility_aabb() const; bool get_use_local_coordinates() const; - float get_speed_scale() const; + real_t get_speed_scale() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -242,17 +242,17 @@ public: void set_direction(Vector3 p_direction); Vector3 get_direction() const; - void set_spread(float p_spread); - float get_spread() const; + void set_spread(real_t p_spread); + real_t get_spread() const; - void set_flatness(float p_flatness); - float get_flatness() const; + void set_flatness(real_t p_flatness); + real_t get_flatness() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_param(Parameter p_param, real_t p_value); + real_t get_param(Parameter p_param) const; - void set_param_randomness(Parameter p_param, float p_value); - float get_param_randomness(Parameter p_param) const; + void set_param_randomness(Parameter p_param, real_t p_value); + real_t get_param_randomness(Parameter p_param) const; void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve); Ref<Curve> get_param_curve(Parameter p_param) const; @@ -267,28 +267,28 @@ public: bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); - void set_emission_sphere_radius(float p_radius); + void set_emission_sphere_radius(real_t p_radius); void set_emission_box_extents(Vector3 p_extents); void set_emission_points(const Vector<Vector3> &p_points); void set_emission_normals(const Vector<Vector3> &p_normals); void set_emission_colors(const Vector<Color> &p_colors); void set_emission_point_count(int p_count); void set_emission_ring_axis(Vector3 p_axis); - void set_emission_ring_height(float p_height); - void set_emission_ring_radius(float p_radius); - void set_emission_ring_inner_radius(float p_radius); + void set_emission_ring_height(real_t p_height); + void set_emission_ring_radius(real_t p_radius); + void set_emission_ring_inner_radius(real_t p_radius); EmissionShape get_emission_shape() const; - float get_emission_sphere_radius() const; + real_t get_emission_sphere_radius() const; Vector3 get_emission_box_extents() const; Vector<Vector3> get_emission_points() const; Vector<Vector3> get_emission_normals() const; Vector<Color> get_emission_colors() const; int get_emission_point_count() const; Vector3 get_emission_ring_axis() const; - float get_emission_ring_height() const; - float get_emission_ring_radius() const; - float get_emission_ring_inner_radius() const; + real_t get_emission_ring_height() const; + real_t get_emission_ring_radius() const; + real_t get_emission_ring_inner_radius() const; void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 05f023721b..c94a99a203 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -53,48 +53,48 @@ Ref<Texture2D> Decal::get_texture(DecalTexture p_type) const { return textures[p_type]; } -void Decal::set_emission_energy(float p_energy) { +void Decal::set_emission_energy(real_t p_energy) { emission_energy = p_energy; RS::get_singleton()->decal_set_emission_energy(decal, emission_energy); } -float Decal::get_emission_energy() const { +real_t Decal::get_emission_energy() const { return emission_energy; } -void Decal::set_albedo_mix(float p_mix) { +void Decal::set_albedo_mix(real_t p_mix) { albedo_mix = p_mix; RS::get_singleton()->decal_set_albedo_mix(decal, albedo_mix); } -float Decal::get_albedo_mix() const { +real_t Decal::get_albedo_mix() const { return albedo_mix; } -void Decal::set_upper_fade(float p_fade) { +void Decal::set_upper_fade(real_t p_fade) { upper_fade = p_fade; RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } -float Decal::get_upper_fade() const { +real_t Decal::get_upper_fade() const { return upper_fade; } -void Decal::set_lower_fade(float p_fade) { +void Decal::set_lower_fade(real_t p_fade) { lower_fade = p_fade; RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } -float Decal::get_lower_fade() const { +real_t Decal::get_lower_fade() const { return lower_fade; } -void Decal::set_normal_fade(float p_fade) { +void Decal::set_normal_fade(real_t p_fade) { normal_fade = p_fade; RS::get_singleton()->decal_set_normal_fade(decal, normal_fade); } -float Decal::get_normal_fade() const { +real_t Decal::get_normal_fade() const { return normal_fade; } @@ -117,21 +117,21 @@ bool Decal::is_distance_fade_enabled() const { return distance_fade_enabled; } -void Decal::set_distance_fade_begin(float p_distance) { +void Decal::set_distance_fade_begin(real_t p_distance) { distance_fade_begin = p_distance; RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length); } -float Decal::get_distance_fade_begin() const { +real_t Decal::get_distance_fade_begin() const { return distance_fade_begin; } -void Decal::set_distance_fade_length(float p_length) { +void Decal::set_distance_fade_length(real_t p_length) { distance_fade_length = p_length; RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length); } -float Decal::get_distance_fade_length() const { +real_t Decal::get_distance_fade_length() const { return distance_fade_length; } diff --git a/scene/3d/decal.h b/scene/3d/decal.h index 31a6315213..041c8f97b2 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -51,16 +51,16 @@ private: RID decal; Vector3 extents = Vector3(1, 1, 1); Ref<Texture2D> textures[TEXTURE_MAX]; - float emission_energy = 1.0; - float albedo_mix = 1.0; + real_t emission_energy = 1.0; + real_t albedo_mix = 1.0; Color modulate = Color(1, 1, 1, 1); uint32_t cull_mask = (1 << 20) - 1; - float normal_fade = 0.0; - float upper_fade = 0.3; - float lower_fade = 0.3; + real_t normal_fade = 0.0; + real_t upper_fade = 0.3; + real_t lower_fade = 0.3; bool distance_fade_enabled = false; - float distance_fade_begin = 10.0; - float distance_fade_length = 1.0; + real_t distance_fade_begin = 10.0; + real_t distance_fade_length = 1.0; protected: static void _bind_methods(); @@ -75,32 +75,32 @@ public: void set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture(DecalTexture p_type) const; - void set_emission_energy(float p_energy); - float get_emission_energy() const; + void set_emission_energy(real_t p_energy); + real_t get_emission_energy() const; - void set_albedo_mix(float p_mix); - float get_albedo_mix() const; + void set_albedo_mix(real_t p_mix); + real_t get_albedo_mix() const; void set_modulate(Color p_modulate); Color get_modulate() const; - void set_upper_fade(float p_energy); - float get_upper_fade() const; + void set_upper_fade(real_t p_energy); + real_t get_upper_fade() const; - void set_lower_fade(float p_fade); - float get_lower_fade() const; + void set_lower_fade(real_t p_fade); + real_t get_lower_fade() const; - void set_normal_fade(float p_fade); - float get_normal_fade() const; + void set_normal_fade(real_t p_fade); + real_t get_normal_fade() const; void set_enable_distance_fade(bool p_enable); bool is_distance_fade_enabled() const; - void set_distance_fade_begin(float p_distance); - float get_distance_fade_begin() const; + void set_distance_fade_begin(real_t p_distance); + real_t get_distance_fade_begin() const; - void set_distance_fade_length(float p_length); - float get_distance_fade_length() const; + void set_distance_fade_length(real_t p_length); + real_t get_distance_fade_length() const; void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index c2943a9606..40f33dfa72 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -38,7 +38,7 @@ bool Light3D::_can_gizmo_scale() const { return false; } -void Light3D::set_param(Param p_param, float p_value) { +void Light3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); param[p_param] = p_value; @@ -53,7 +53,7 @@ void Light3D::set_param(Param p_param, float p_value) { } } -float Light3D::get_param(Param p_param) const { +real_t Light3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return param[p_param]; } @@ -128,8 +128,8 @@ AABB Light3D::get_aabb() const { return AABB(Vector3(-1, -1, -1) * param[PARAM_RANGE], Vector3(2, 2, 2) * param[PARAM_RANGE]); } else if (type == RenderingServer::LIGHT_SPOT) { - float len = param[PARAM_RANGE]; - float size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len; + real_t len = param[PARAM_RANGE]; + real_t size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index d0308a3025..51ed2fcf50 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -71,7 +71,7 @@ public: private: Color color; - float param[PARAM_MAX] = {}; + real_t param[PARAM_MAX] = {}; Color shadow_color; bool shadow = false; bool negative = false; @@ -102,8 +102,8 @@ public: void set_editor_only(bool p_editor_only); bool is_editor_only() const; - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; void set_shadow(bool p_enable); bool has_shadow() const; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 0daee69ee5..92402c9a6a 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -588,31 +588,31 @@ bool Node3D::is_visible() const { return data.visible; } -void Node3D::rotate_object_local(const Vector3 &p_axis, float p_angle) { +void Node3D::rotate_object_local(const Vector3 &p_axis, real_t p_angle) { Transform3D t = get_transform(); t.basis.rotate_local(p_axis, p_angle); set_transform(t); } -void Node3D::rotate(const Vector3 &p_axis, float p_angle) { +void Node3D::rotate(const Vector3 &p_axis, real_t p_angle) { Transform3D t = get_transform(); t.basis.rotate(p_axis, p_angle); set_transform(t); } -void Node3D::rotate_x(float p_angle) { +void Node3D::rotate_x(real_t p_angle) { Transform3D t = get_transform(); t.basis.rotate(Vector3(1, 0, 0), p_angle); set_transform(t); } -void Node3D::rotate_y(float p_angle) { +void Node3D::rotate_y(real_t p_angle) { Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 1, 0), p_angle); set_transform(t); } -void Node3D::rotate_z(float p_angle) { +void Node3D::rotate_z(real_t p_angle) { Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 0, 1), p_angle); set_transform(t); @@ -644,7 +644,7 @@ void Node3D::scale_object_local(const Vector3 &p_scale) { set_transform(t); } -void Node3D::global_rotate(const Vector3 &p_axis, float p_angle) { +void Node3D::global_rotate(const Vector3 &p_axis, real_t p_angle) { Transform3D t = get_global_transform(); t.basis.rotate(p_axis, p_angle); set_global_transform(t); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 282f4805cc..da54452abb 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -167,18 +167,18 @@ public: Transform3D get_relative_transform(const Node *p_parent) const; - void rotate(const Vector3 &p_axis, float p_angle); - void rotate_x(float p_angle); - void rotate_y(float p_angle); - void rotate_z(float p_angle); + void rotate(const Vector3 &p_axis, real_t p_angle); + void rotate_x(real_t p_angle); + void rotate_y(real_t p_angle); + void rotate_z(real_t p_angle); void translate(const Vector3 &p_offset); void scale(const Vector3 &p_ratio); - void rotate_object_local(const Vector3 &p_axis, float p_angle); + void rotate_object_local(const Vector3 &p_axis, real_t p_angle); void scale_object_local(const Vector3 &p_scale); void translate_object_local(const Vector3 &p_offset); - void global_rotate(const Vector3 &p_axis, float p_angle); + void global_rotate(const Vector3 &p_axis, real_t p_angle); void global_scale(const Vector3 &p_scale); void global_translate(const Vector3 &p_offset); diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 6af2e7f879..6818acf69d 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -94,17 +94,24 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { return; } - float bl = c->get_baked_length(); + real_t bl = c->get_baked_length(); if (bl == 0.0) { return; } - float bi = c->get_bake_interval(); - float o_next = offset + bi; + real_t bi = c->get_bake_interval(); + real_t o_next = offset + bi; + real_t o_prev = offset - bi; if (loop) { o_next = Math::fposmod(o_next, bl); - } else if (rotation_mode == ROTATION_ORIENTED && o_next >= bl) { - o_next = bl; + o_prev = Math::fposmod(o_prev, bl); + } else if (rotation_mode == ROTATION_ORIENTED) { + if (o_next >= bl) { + o_next = bl; + } + if (o_prev <= 0) { + o_prev = 0; + } } Vector3 pos = c->interpolate_baked(offset, cubic); @@ -115,6 +122,11 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { if (rotation_mode == ROTATION_ORIENTED) { Vector3 forward = c->interpolate_baked(o_next, cubic) - pos; + // Try with the previous position + if (forward.length_squared() < CMP_EPSILON2) { + forward = pos - c->interpolate_baked(o_prev, cubic); + } + if (forward.length_squared() < CMP_EPSILON2) { forward = Vector3(0, 0, 1); } else { @@ -157,8 +169,8 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); Vector3 axis = t_prev.cross(t_cur); - float dot = t_prev.dot(t_cur); - float angle = Math::acos(CLAMP(dot, -1, 1)); + real_t dot = t_prev.dot(t_cur); + real_t angle = Math::acos(CLAMP(dot, -1, 1)); if (likely(!Math::is_zero_approx(angle))) { if (rotation_mode == ROTATION_Y) { @@ -177,7 +189,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } // do the additional tilting - float tilt_angle = c->interpolate_baked_tilt(offset); + real_t tilt_angle = c->interpolate_baked_tilt(offset); Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { @@ -232,7 +244,7 @@ bool PathFollow3D::get_cubic_interpolation() const { void PathFollow3D::_validate_property(PropertyInfo &property) const { if (property.name == "offset") { - float max = 10000; + real_t max = 10000; if (path && path->get_curve().is_valid()) { max = path->get_curve()->get_baked_length(); } @@ -295,13 +307,13 @@ void PathFollow3D::_bind_methods() { BIND_ENUM_CONSTANT(ROTATION_ORIENTED); } -void PathFollow3D::set_offset(float p_offset) { +void PathFollow3D::set_offset(real_t p_offset) { delta_offset = p_offset - offset; offset = p_offset; if (path) { if (path->get_curve().is_valid()) { - float path_length = path->get_curve()->get_baked_length(); + real_t path_length = path->get_curve()->get_baked_length(); if (loop) { offset = Math::fposmod(offset, path_length); @@ -317,39 +329,39 @@ void PathFollow3D::set_offset(float p_offset) { } } -void PathFollow3D::set_h_offset(float p_h_offset) { +void PathFollow3D::set_h_offset(real_t p_h_offset) { h_offset = p_h_offset; if (path) { _update_transform(); } } -float PathFollow3D::get_h_offset() const { +real_t PathFollow3D::get_h_offset() const { return h_offset; } -void PathFollow3D::set_v_offset(float p_v_offset) { +void PathFollow3D::set_v_offset(real_t p_v_offset) { v_offset = p_v_offset; if (path) { _update_transform(); } } -float PathFollow3D::get_v_offset() const { +real_t PathFollow3D::get_v_offset() const { return v_offset; } -float PathFollow3D::get_offset() const { +real_t PathFollow3D::get_offset() const { return offset; } -void PathFollow3D::set_unit_offset(float p_unit_offset) { +void PathFollow3D::set_unit_offset(real_t p_unit_offset) { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { set_offset(p_unit_offset * path->get_curve()->get_baked_length()); } } -float PathFollow3D::get_unit_offset() const { +real_t PathFollow3D::get_unit_offset() const { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { return get_offset() / path->get_curve()->get_baked_length(); } else { diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 8545370a4a..1ffe291100 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -83,17 +83,17 @@ protected: static void _bind_methods(); public: - void set_offset(float p_offset); - float get_offset() const; + void set_offset(real_t p_offset); + real_t get_offset() const; - void set_h_offset(float p_h_offset); - float get_h_offset() const; + void set_h_offset(real_t p_h_offset); + real_t get_h_offset() const; - void set_v_offset(float p_v_offset); - float get_v_offset() const; + void set_v_offset(real_t p_v_offset); + real_t get_v_offset() const; - void set_unit_offset(float p_unit_offset); - float get_unit_offset() const; + void set_unit_offset(real_t p_unit_offset); + real_t get_unit_offset() const; void set_loop(bool p_loop); bool has_loop() const; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 93ecb2cd3a..100e3563a3 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -1347,7 +1347,7 @@ int CharacterBody3D::get_max_slides() const { } void CharacterBody3D::set_max_slides(int p_max_slides) { - ERR_FAIL_COND(p_max_slides > 0); + ERR_FAIL_COND(p_max_slides < 1); max_slides = p_max_slides; } @@ -1422,7 +1422,7 @@ void CharacterBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stop_on_slope"), "set_stop_on_slope_enabled", "is_stop_on_slope_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "infinite_inertia"), "set_infinite_inertia_enabled", "is_infinite_inertia_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides"), "set_max_slides", "get_max_slides"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_RANGE, "1,8,1,or_greater"), "set_max_slides", "get_max_slides"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); diff --git a/scene/3d/proximity_group_3d.h b/scene/3d/proximity_group_3d.h index 05aa00b228..e45adc3040 100644 --- a/scene/3d/proximity_group_3d.h +++ b/scene/3d/proximity_group_3d.h @@ -49,7 +49,7 @@ private: DispatchMode dispatch_mode = MODE_PROXY; Vector3 grid_radius = Vector3(1, 1, 1); - float cell_size = 1.0; + real_t cell_size = 1.0; uint32_t group_version = 0; void _clear_groups(); diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 9ce4c37457..86e2af7df5 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -366,7 +366,7 @@ void Skeleton3D::clear_bones_global_pose_override() { _make_dirty(); } -void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, float p_amount, bool p_persistent) { +void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { ERR_FAIL_INDEX(p_bone, bones.size()); bones.write[p_bone].global_pose_override_amount = p_amount; bones.write[p_bone].global_pose_override = p_pose; diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 2f6e416c8c..3fdf5321a5 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -85,7 +85,7 @@ private: bool custom_pose_enable = false; Transform3D custom_pose; - float global_pose_override_amount = 0.0; + real_t global_pose_override_amount = 0.0; bool global_pose_override_reset = false; Transform3D global_pose_override; @@ -147,7 +147,7 @@ public: Transform3D get_bone_global_pose_no_override(int p_bone) const; void clear_bones_global_pose_override(); - void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, float p_amount, bool p_persistent = false); + void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 1005d51e63..a891566633 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -85,7 +85,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); } - BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + BoneId middle_chain_item_id = (BoneId)(sub_chain_size * 0.5); // Build chain by reading chain ids in reverse order // For each chain item id will be created a ChainItem if doesn't exists diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index a901920dbe..9baf5d6baf 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -132,12 +132,12 @@ Color SpriteBase3D::get_modulate() const { return modulate; } -void SpriteBase3D::set_pixel_size(float p_amount) { +void SpriteBase3D::set_pixel_size(real_t p_amount) { pixel_size = p_amount; _queue_update(); } -float SpriteBase3D::get_pixel_size() const { +real_t SpriteBase3D::get_pixel_size() const { return pixel_size; } @@ -203,7 +203,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { return Ref<TriangleMesh>(); } - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { @@ -470,7 +470,7 @@ void Sprite3D::_draw() { Color color = _get_color_accum(); color.a *= get_opacity(); - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { @@ -837,7 +837,7 @@ void AnimatedSprite3D::_draw() { Color color = _get_color_accum(); color.a *= get_opacity(); - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 404ef57e6a..e243a96ca0 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -72,7 +72,7 @@ private: float opacity = 1.0; Vector3::Axis axis = Vector3::AXIS_Z; - float pixel_size = 0.01; + real_t pixel_size = 0.01; AABB aabb; RID mesh; @@ -130,8 +130,8 @@ public: void set_opacity(float p_amount); float get_opacity() const; - void set_pixel_size(float p_amount); - float get_pixel_size() const; + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; void set_axis(Vector3::Axis p_axis); Vector3::Axis get_axis() const; diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index f1b9708f91..6124326d2d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -56,20 +56,20 @@ static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 Vector3 v1 = p_vtx[2] - p_vtx[0]; Vector3 v2 = p_pos - p_vtx[0]; - float d00 = v0.dot(v0); - float d01 = v0.dot(v1); - float d11 = v1.dot(v1); - float d20 = v2.dot(v0); - float d21 = v2.dot(v1); - float denom = (d00 * d11 - d01 * d01); + real_t d00 = v0.dot(v0); + real_t d01 = v0.dot(v1); + real_t d11 = v1.dot(v1); + real_t d20 = v2.dot(v0); + real_t d21 = v2.dot(v1); + real_t denom = (d00 * d11 - d01 * d01); if (denom == 0) { r_uv = p_uv[0]; r_normal = p_normal[0]; return; } - float v = (d11 * d20 - d01 * d21) / denom; - float w = (d00 * d21 - d01 * d20) / denom; - float u = 1.0f - v - w; + real_t v = (d11 * d20 - d01 * d21) / denom; + real_t w = (d00 * d21 - d01 * d20) / denom; + real_t u = 1.0f - v - w; r_uv = p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; r_normal = (p_normal[0] * u + p_normal[1] * v + p_normal[2] * w).normalized(); @@ -81,7 +81,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //find best axis to map to, for scanning values int closest_axis = 0; - float closest_dot = 0; + real_t closest_dot = 0; Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]); Vector3 normal = plane.normal; @@ -89,7 +89,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co for (int i = 0; i < 3; i++) { Vector3 axis; axis[i] = 1.0; - float dot = ABS(normal.dot(axis)); + real_t dot = ABS(normal.dot(axis)); if (i == 0 || dot > closest_dot) { closest_axis = i; closest_dot = dot; @@ -103,8 +103,8 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector3 t2; t2[(closest_axis + 2) % 3] = 1.0; - t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width); - t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width); + t1 *= p_aabb.size[(closest_axis + 1) % 3] / real_t(color_scan_cell_width); + t2 *= p_aabb.size[(closest_axis + 2) % 3] / real_t(color_scan_cell_width); Color albedo_accum; Color emission_accum; @@ -114,10 +114,10 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //map to a grid average in the best axis for this face for (int i = 0; i < color_scan_cell_width; i++) { - Vector3 ofs_i = float(i) * t1; + Vector3 ofs_i = real_t(i) * t1; for (int j = 0; j < color_scan_cell_width; j++) { - Vector3 ofs_j = float(j) * t2; + Vector3 ofs_j = real_t(j) * t2; Vector3 from = p_aabb.position + ofs_i + ofs_j; Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis]; @@ -155,8 +155,8 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co lnormal = normal; } - int uv_x = CLAMP(int(Math::fposmod(uv.x, 1.0f) * bake_texture_size), 0, bake_texture_size - 1); - int uv_y = CLAMP(int(Math::fposmod(uv.y, 1.0f) * bake_texture_size), 0, bake_texture_size - 1); + int uv_x = CLAMP(int(Math::fposmod(uv.x, (real_t)1.0) * bake_texture_size), 0, bake_texture_size - 1); + int uv_y = CLAMP(int(Math::fposmod(uv.y, (real_t)1.0) * bake_texture_size), 0, bake_texture_size - 1); int ofs = uv_y * bake_texture_size + uv_x; albedo_accum.r += p_material.albedo[ofs].r; @@ -187,8 +187,8 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co lnormal = normal; } - int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); - int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); + int uv_x = CLAMP(Math::fposmod(uv.x, (real_t)1.0) * bake_texture_size, 0, bake_texture_size - 1); + int uv_y = CLAMP(Math::fposmod(uv.y, (real_t)1.0) * bake_texture_size, 0, bake_texture_size - 1); int ofs = uv_y * bake_texture_size + uv_x; @@ -636,7 +636,7 @@ void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { } axis_cell_size[i] = axis_cell_size[longest_axis]; - float axis_size = po2_bounds.size[longest_axis]; + real_t axis_size = po2_bounds.size[longest_axis]; //shrink until fit subdiv while (axis_size / 2.0 >= po2_bounds.size[i]) { @@ -954,7 +954,7 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() { Vector3 face_points[4]; for (int j = 0; j < 4; j++) { - float v[3]; + real_t v[3]; v[0] = 1.0; v[1] = 1 - 2 * ((j >> 1) & 1); v[2] = v[1] * (1 - 2 * (j & 1)); diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index b04c7e2858..e620f33478 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -124,7 +124,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { return res; }; -Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) const { +Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, Vector3()); @@ -544,7 +544,7 @@ void XROrigin3D::clear_tracked_camera_if(XRCamera3D *p_tracked_camera) { }; }; -float XROrigin3D::get_world_scale() const { +real_t XROrigin3D::get_world_scale() const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 1.0); @@ -552,7 +552,7 @@ float XROrigin3D::get_world_scale() const { return xr_server->get_world_scale(); }; -void XROrigin3D::set_world_scale(float p_world_scale) { +void XROrigin3D::set_world_scale(real_t p_world_scale) { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 90079f5fe9..312287a93b 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -54,7 +54,7 @@ public: virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override; virtual Point2 unproject_position(const Vector3 &p_pos) const override; - virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const override; + virtual Vector3 project_position(const Point2 &p_point, real_t p_z_depth) const override; virtual Vector<Plane> get_frustum() const override; XRCamera3D() {} @@ -163,8 +163,8 @@ public: void set_tracked_camera(XRCamera3D *p_tracked_camera); void clear_tracked_camera_if(XRCamera3D *p_tracked_camera); - float get_world_scale() const; - void set_world_scale(float p_world_scale); + real_t get_world_scale() const; + void set_world_scale(real_t p_world_scale); XROrigin3D() {} ~XROrigin3D() {} diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index a57e986877..542011618d 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -140,6 +140,8 @@ bool Tween::is_valid() { } Ref<Tween> Tween::bind_node(Node *p_node) { + ERR_FAIL_NULL_V(p_node, this); + bound_node = p_node->get_instance_id(); is_bound = true; return this; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 75a4464a40..82f4a216b8 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -345,7 +345,7 @@ void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) { return; } - if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) { + if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) { on_action_event(p_event); accept_event(); } @@ -353,7 +353,7 @@ void BaseButton::_unhandled_key_input(Ref<InputEvent> p_event) { String BaseButton::get_tooltip(const Point2 &p_pos) const { String tooltip = Control::get_tooltip(p_pos); - if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->is_valid()) { + if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) { String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")"; if (tooltip != String() && shortcut->get_name().nocasecmp_to(tooltip) != 0) { text += "\n" + tooltip; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index be5e0bf4e5..32922f609d 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -72,13 +72,34 @@ void CodeEdit::_notification(int p_what) { code_completion_background_color = get_theme_color(SNAME("completion_background_color")); code_completion_selected_color = get_theme_color(SNAME("completion_selected_color")); code_completion_existing_color = get_theme_color(SNAME("completion_existing_color")); + + line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color")); } break; case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); + const Size2 size = get_size(); const bool caret_visible = is_caret_visible(); const bool rtl = is_layout_rtl(); const int row_height = get_row_height(); + if (line_length_guideline_columns.size() > 0) { + const int xmargin_beg = cache.style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); + const int xmargin_end = size.width - cache.style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); + const int char_size = (int)cache.font->get_char_size('0', 0, cache.font_size).width; + + for (int i = 0; i < line_length_guideline_columns.size(); i++) { + const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll(); + if (xoffset > xmargin_beg && xoffset < xmargin_end) { + Color guideline_color = (i == 0) ? line_length_guideline_color : line_length_guideline_color * Color(1, 1, 1, 0.5); + if (rtl) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - xoffset, 0), Point2(size.width - xoffset, size.height), guideline_color); + continue; + } + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(xoffset, 0), Point2(xoffset, size.height), guideline_color); + } + } + } + bool code_completion_below = false; if (caret_visible && code_completion_active && code_completion_options.size() > 0) { Ref<StyleBox> csb = get_theme_stylebox(SNAME("completion")); @@ -204,8 +225,15 @@ void CodeEdit::_notification(int p_what) { round_ofs = round_ofs.round(); draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, cache.font_size, font_color); if (end > 0) { - Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing - 1); - draw_line(b, b + Vector2(end - begin, 0), font_color); + // Draw an underline for the currently edited function parameter. + const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing); + draw_line(b, b + Vector2(end - begin, 0), font_color, 2); + + // Draw a translucent text highlight as well. + const Rect2 highlight_rect = Rect2( + hint_ofs + sb->get_offset() + Vector2(begin, 0), + Vector2(end - begin, font_height)); + draw_rect(highlight_rect, font_color * Color(1, 1, 1, 0.2)); } line_spacing += cache.line_spacing; } @@ -278,6 +306,39 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } } + } else { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_command_pressed() && symbol_lookup_word != String()) { + Vector2i mpos = mb->get_position(); + if (is_layout_rtl()) { + mpos.x = get_size().x - mpos.x; + } + int line, col; + _get_mouse_pos(Point2i(mpos.x, mpos.y), line, col); + + emit_signal(SNAME("symbol_lookup"), symbol_lookup_word, line, col); + return; + } + } + } + } + + Ref<InputEventMouseMotion> mm = p_gui_input; + if (mm.is_valid()) { + Vector2i mpos = mm->get_position(); + if (is_layout_rtl()) { + mpos.x = get_size().x - mpos.x; + } + + if (symbol_lookup_on_click_enabled) { + if (mm->is_command_pressed() && mm->get_button_mask() == 0 && !is_dragging_cursor()) { + symbol_lookup_new_word = get_word_at_pos(mpos); + if (symbol_lookup_new_word != symbol_lookup_word) { + emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word); + } + } else { + set_symbol_lookup_word_as_valid(false); + } } } @@ -288,6 +349,25 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } + /* Ctrl + Hover symbols */ +#ifdef OSX_ENABLED + if (k->get_keycode() == KEY_META) { +#else + if (k->get_keycode() == KEY_CTRL) { +#endif + if (symbol_lookup_on_click_enabled) { + if (k->is_pressed() && !is_dragging_cursor()) { + symbol_lookup_new_word = get_word_at_pos(_get_local_mouse_pos()); + if (symbol_lookup_new_word != symbol_lookup_word) { + emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word); + } + } else { + set_symbol_lookup_word_as_valid(false); + } + } + return; + } + /* If a modifier has been pressed, and nothing else, return. */ if (!k->is_pressed() || k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) { return; @@ -437,7 +517,12 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } +/* General overrides */ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { + if (symbol_lookup_word != String()) { + return CURSOR_POINTING_HAND; + } + if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || get_line_count() == 0))) { return CURSOR_ARROW; } @@ -459,6 +544,58 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { return TextEdit::get_cursor_shape(p_pos); } +void CodeEdit::handle_unicode_input(uint32_t p_unicode) { + bool had_selection = is_selection_active(); + if (had_selection) { + begin_complex_operation(); + delete_selection(); + } + + // Remove the old character if in insert mode and no selection. + if (is_insert_mode() && !had_selection) { + begin_complex_operation(); + + // Make sure we don't try and remove empty space. + if (cursor_get_column() < get_line(cursor_get_line()).length()) { + _remove_text(cursor_get_line(), cursor_get_column(), cursor_get_line(), cursor_get_column() + 1); + } + } + + const char32_t chr[2] = { (char32_t)p_unicode, 0 }; + + if (auto_brace_completion_enabled) { + int cl = cursor_get_line(); + int cc = cursor_get_column(); + int caret_move_offset = 1; + + int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1; + + if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) { + insert_text_at_cursor(chr); + } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) { + insert_text_at_cursor(chr); + } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) { + caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length(); + } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) { + insert_text_at_cursor(chr); + } else { + insert_text_at_cursor(chr); + + int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1); + if (pre_brace_pair != -1) { + insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key); + } + } + cursor_set_column(cc + caret_move_offset); + } else { + insert_text_at_cursor(chr); + } + + if ((is_insert_mode() && !had_selection) || (had_selection)) { + end_complex_operation(); + } +} + /* Indent management */ void CodeEdit::set_indent_size(const int p_size) { ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); @@ -527,13 +664,13 @@ void CodeEdit::do_indent() { } if (!indent_using_spaces) { - _insert_text_at_cursor("\t"); + insert_text_at_cursor("\t"); return; } int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor_get_column()); if (spaces_to_add > 0) { - _insert_text_at_cursor(String(" ").repeat(spaces_to_add)); + insert_text_at_cursor(String(" ").repeat(spaces_to_add)); } } @@ -713,34 +850,6 @@ int CodeEdit::_calculate_spaces_till_next_right_indent(int p_column) const { return indent_size - p_column % indent_size; } -/* TODO: remove once brace completion is refactored. */ -static char32_t _get_right_pair_symbol(char32_t c) { - if (c == '"') { - return '"'; - } - if (c == '\'') { - return '\''; - } - if (c == '(') { - return ')'; - } - if (c == '[') { - return ']'; - } - if (c == '{') { - return '}'; - } - return 0; -} - -static bool _is_pair_left_symbol(char32_t c) { - return c == '"' || - c == '\'' || - c == '(' || - c == '[' || - c == '{'; -} - void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { if (is_readonly()) { return; @@ -803,9 +912,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { if (should_indent) { ins += indent_text; - /* TODO: Change when brace completion is refactored. */ - char32_t closing_char = _get_right_pair_symbol(indent_char); - if (closing_char != 0 && closing_char == line[cc]) { + String closing_pair = get_auto_brace_completion_close_key(String::chr(indent_char)); + if (!closing_pair.is_empty() && line.find(closing_pair, cc) == cc) { /* No need to move the brace below if we are not taking the text with us. */ if (p_split_current_line) { brace_indent = true; @@ -873,12 +981,20 @@ void CodeEdit::backspace() { merge_gutters(cl, prev_line); - /* TODO: Change when brace completion is refactored. */ - if (auto_brace_completion_enabled && cc > 0 && _is_pair_left_symbol(get_line(cl)[cc - 1])) { - _consume_backspace_for_pair_symbol(prev_line, prev_column); - cursor_set_line(prev_line, false, true); - cursor_set_column(prev_column); - return; + if (auto_brace_completion_enabled && cc > 0) { + int idx = _get_auto_brace_pair_open_at_pos(cl, cc); + if (idx != -1) { + prev_column = cc - auto_brace_completion_pairs[idx].open_key.length(); + + if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) { + _remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length()); + } else { + _remove_text(prev_line, prev_column, cl, cc); + } + cursor_set_line(prev_line, false, true); + cursor_set_column(prev_column); + return; + } } /* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */ @@ -896,6 +1012,93 @@ void CodeEdit::backspace() { cursor_set_column(prev_column); } +/* Auto brace completion */ +void CodeEdit::set_auto_brace_completion_enabled(bool p_enabled) { + auto_brace_completion_enabled = p_enabled; +} + +bool CodeEdit::is_auto_brace_completion_enabled() const { + return auto_brace_completion_enabled; +} + +void CodeEdit::set_highlight_matching_braces_enabled(bool p_enabled) { + highlight_matching_braces_enabled = p_enabled; + update(); +} + +bool CodeEdit::is_highlight_matching_braces_enabled() const { + return highlight_matching_braces_enabled; +} + +void CodeEdit::add_auto_brace_completion_pair(const String &p_open_key, const String &p_close_key) { + ERR_FAIL_COND_MSG(p_open_key.is_empty(), "auto brace completion open key cannot be empty"); + ERR_FAIL_COND_MSG(p_close_key.is_empty(), "auto brace completion close key cannot be empty"); + + for (int i = 0; i < p_open_key.length(); i++) { + ERR_FAIL_COND_MSG(!is_symbol(p_open_key[i]), "auto brace completion open key must be a symbol"); + } + for (int i = 0; i < p_close_key.length(); i++) { + ERR_FAIL_COND_MSG(!is_symbol(p_close_key[i]), "auto brace completion close key must be a symbol"); + } + + int at = 0; + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + ERR_FAIL_COND_MSG(auto_brace_completion_pairs[i].open_key == p_open_key, "auto brace completion open key '" + p_open_key + "' already exists."); + if (p_open_key.length() < auto_brace_completion_pairs[i].open_key.length()) { + at++; + } + } + + BracePair brace_pair; + brace_pair.open_key = p_open_key; + brace_pair.close_key = p_close_key; + auto_brace_completion_pairs.insert(at, brace_pair); +} + +void CodeEdit::set_auto_brace_completion_pairs(const Dictionary &p_auto_brace_completion_pairs) { + auto_brace_completion_pairs.clear(); + + Array keys = p_auto_brace_completion_pairs.keys(); + for (int i = 0; i < keys.size(); i++) { + add_auto_brace_completion_pair(keys[i], p_auto_brace_completion_pairs[keys[i]]); + } +} + +Dictionary CodeEdit::get_auto_brace_completion_pairs() const { + Dictionary brace_pairs; + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + brace_pairs[auto_brace_completion_pairs[i].open_key] = auto_brace_completion_pairs[i].close_key; + } + return brace_pairs; +} + +bool CodeEdit::has_auto_brace_completion_open_key(const String &p_open_key) const { + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + if (auto_brace_completion_pairs[i].open_key == p_open_key) { + return true; + } + } + return false; +} + +bool CodeEdit::has_auto_brace_completion_close_key(const String &p_close_key) const { + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + if (auto_brace_completion_pairs[i].close_key == p_close_key) { + return true; + } + } + return false; +} + +String CodeEdit::get_auto_brace_completion_close_key(const String &p_open_key) const { + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + if (auto_brace_completion_pairs[i].open_key == p_open_key) { + return auto_brace_completion_pairs[i].close_key; + } + } + return String(); +} + /* Main Gutter */ void CodeEdit::_update_draw_main_gutter() { set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines); @@ -1700,35 +1903,40 @@ void CodeEdit::confirm_code_completion(bool p_replace) { insert_text_at_cursor(insert_text.substr(matching_chars)); } - /* TODO: merge with autobrace completion, when in CodeEdit. */ /* Handle merging of symbols eg strings, brackets. */ const String line = get_line(caret_line); char32_t next_char = line[cursor_get_column()]; char32_t last_completion_char = insert_text[insert_text.length() - 1]; char32_t last_completion_char_display = display_text[display_text.length() - 1]; - if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) { + int pre_brace_pair = cursor_get_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column()) : -1; + int post_brace_pair = cursor_get_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column()) : -1; + + if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) { _remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1); } - if (last_completion_char == '(') { - if (next_char == last_completion_char) { - _remove_text(caret_line, cursor_get_column() - 1, caret_line, cursor_get_column()); - } else if (auto_brace_completion_enabled) { - insert_text_at_cursor(")"); - cursor_set_column(cursor_get_column() - 1); - } - } else if (last_completion_char == ')' && next_char == '(') { - _remove_text(caret_line, cursor_get_column() - 2, caret_line, cursor_get_column()); - if (line[cursor_get_column() + 1] != ')') { - cursor_set_column(cursor_get_column() - 1); + if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) { + _remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1); + } else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) { + insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key); + cursor_set_column(cursor_get_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length()); + } + + if (pre_brace_pair == -1 && post_brace_pair == -1 && cursor_get_column() > 0 && cursor_get_column() < get_line(caret_line).length()) { + pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column() + 1); + if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1)) { + _remove_text(caret_line, cursor_get_column() - 2, caret_line, cursor_get_column()); + if (_get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1) != pre_brace_pair) { + cursor_set_column(cursor_get_column() - 1); + } } } end_complex_operation(); cancel_code_completion(); - if (last_completion_char == '(') { + if (code_completion_prefixes.has(String::chr(last_completion_char))) { request_code_completion(); } } @@ -1742,6 +1950,58 @@ void CodeEdit::cancel_code_completion() { update(); } +/* Line length guidelines */ +void CodeEdit::set_line_length_guidelines(TypedArray<int> p_guideline_columns) { + line_length_guideline_columns = p_guideline_columns; + update(); +} + +TypedArray<int> CodeEdit::get_line_length_guidelines() const { + return line_length_guideline_columns; +} + +/* Symbol lookup */ +void CodeEdit::set_symbol_lookup_on_click_enabled(bool p_enabled) { + symbol_lookup_on_click_enabled = p_enabled; + set_symbol_lookup_word_as_valid(false); +} + +bool CodeEdit::is_symbol_lookup_on_click_enabled() const { + return symbol_lookup_on_click_enabled; +} + +String CodeEdit::get_text_for_symbol_lookup() { + int line, col; + Point2i mp = _get_local_mouse_pos(); + _get_mouse_pos(mp, line, col); + + StringBuilder lookup_text; + const int text_size = get_line_count(); + for (int i = 0; i < text_size; i++) { + String text = get_line(i); + + if (i == line) { + lookup_text += text.substr(0, col); + /* Not unicode, represents the cursor. */ + lookup_text += String::chr(0xFFFF); + lookup_text += text.substr(col, text.size()); + } else { + lookup_text += text; + } + + if (i != text_size - 1) { + lookup_text += "\n"; + } + } + return lookup_text.as_string(); +} + +void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) { + symbol_lookup_word = p_valid ? symbol_lookup_new_word : ""; + symbol_lookup_new_word = ""; + _set_symbol_lookup_word(symbol_lookup_word); +} + void CodeEdit::_bind_methods() { /* Indent management */ ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size); @@ -1762,6 +2022,22 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines); ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines); + /* Auto brace completion */ + ClassDB::bind_method(D_METHOD("set_auto_brace_completion_enabled", "enable"), &CodeEdit::set_auto_brace_completion_enabled); + ClassDB::bind_method(D_METHOD("is_auto_brace_completion_enabled"), &CodeEdit::is_auto_brace_completion_enabled); + + ClassDB::bind_method(D_METHOD("set_highlight_matching_braces_enabled", "enable"), &CodeEdit::set_highlight_matching_braces_enabled); + ClassDB::bind_method(D_METHOD("is_highlight_matching_braces_enabled"), &CodeEdit::is_highlight_matching_braces_enabled); + + ClassDB::bind_method(D_METHOD("add_auto_brace_completion_pair", "start_key", "end_key"), &CodeEdit::add_auto_brace_completion_pair); + ClassDB::bind_method(D_METHOD("set_auto_brace_completion_pairs", "pairs"), &CodeEdit::set_auto_brace_completion_pairs); + ClassDB::bind_method(D_METHOD("get_auto_brace_completion_pairs"), &CodeEdit::get_auto_brace_completion_pairs); + + ClassDB::bind_method(D_METHOD("has_auto_brace_completion_open_key", "open_key"), &CodeEdit::has_auto_brace_completion_open_key); + ClassDB::bind_method(D_METHOD("has_auto_brace_completion_close_key", "close_key"), &CodeEdit::has_auto_brace_completion_close_key); + + ClassDB::bind_method(D_METHOD("get_auto_brace_completion_close_key", "open_key"), &CodeEdit::get_auto_brace_completion_close_key); + /* Main Gutter */ ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback); @@ -1890,19 +2166,35 @@ void CodeEdit::_bind_methods() { BIND_VMETHOD(MethodInfo("_request_code_completion", PropertyInfo(Variant::BOOL, "force"))); BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_filter_code_completion_candidates", PropertyInfo(Variant::ARRAY, "candidates"))); + /* Line length guidelines */ + ClassDB::bind_method(D_METHOD("set_line_length_guidelines", "guideline_columns"), &CodeEdit::set_line_length_guidelines); + ClassDB::bind_method(D_METHOD("get_line_length_guidelines"), &CodeEdit::get_line_length_guidelines); + + /* Symbol lookup */ + ClassDB::bind_method(D_METHOD("set_symbol_lookup_on_click_enabled", "enable"), &CodeEdit::set_symbol_lookup_on_click_enabled); + ClassDB::bind_method(D_METHOD("is_symbol_lookup_on_click_enabled"), &CodeEdit::is_symbol_lookup_on_click_enabled); + + ClassDB::bind_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::get_text_for_symbol_lookup); + + ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid); + /* Inspector */ - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_lookup_on_click"), "set_symbol_lookup_on_click_enabled", "is_symbol_lookup_on_click_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "line_length_guidelines"), "set_line_length_guidelines", "get_line_length_guidelines"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter"); + ADD_GROUP("Gutters", "gutters_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); ADD_GROUP("Delimiters", "delimiter_"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_strings"), "set_string_delimiters", "get_string_delimiters"); @@ -1918,11 +2210,74 @@ void CodeEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes"); + ADD_GROUP("Auto brace completion", "auto_brace_completion_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_enabled"), "set_auto_brace_completion_enabled", "is_auto_brace_completion_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_highlight_matching"), "set_highlight_matching_braces_enabled", "is_highlight_matching_braces_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "auto_brace_completion_pairs"), "set_auto_brace_completion_pairs", "get_auto_brace_completion_pairs"); + /* Signals */ + /* Gutters */ ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line"))); + + /* Code Completion */ ADD_SIGNAL(MethodInfo("request_code_completion")); + + /* Symbol lookup */ + ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column"))); + ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol"))); } +/* Auto brace completion */ +int CodeEdit::_get_auto_brace_pair_open_at_pos(int p_line, int p_col) { + const String &line = get_line(p_line); + + /* Should be fast enough, expecting low amount of pairs... */ + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + const String &open_key = auto_brace_completion_pairs[i].open_key; + if (p_col - open_key.length() < 0) { + continue; + } + + bool is_match = true; + for (int j = 0; j < open_key.length(); j++) { + if (line[(p_col - 1) - j] != open_key[(open_key.length() - 1) - j]) { + is_match = false; + break; + } + } + + if (is_match) { + return i; + } + } + return -1; +} + +int CodeEdit::_get_auto_brace_pair_close_at_pos(int p_line, int p_col) { + const String &line = get_line(p_line); + + /* Should be fast enough, expecting low amount of pairs... */ + for (int i = 0; i < auto_brace_completion_pairs.size(); i++) { + if (p_col + auto_brace_completion_pairs[i].close_key.length() > line.length()) { + continue; + } + + bool is_match = true; + for (int j = 0; j < auto_brace_completion_pairs[i].close_key.length(); j++) { + if (line[p_col + j] != auto_brace_completion_pairs[i].close_key[j]) { + is_match = false; + break; + } + } + + if (is_match) { + return i; + } + } + return -1; +} + +/* Gutters */ void CodeEdit::_gutter_clicked(int p_line, int p_gutter) { if (p_gutter == main_gutter) { if (draw_breakpoints) { @@ -2547,6 +2902,17 @@ CodeEdit::CodeEdit() { auto_indent_prefixes.insert('['); auto_indent_prefixes.insert('('); + /* Auto brace completion */ + add_auto_brace_completion_pair("(", ")"); + add_auto_brace_completion_pair("{", "}"); + add_auto_brace_completion_pair("[", "]"); + add_auto_brace_completion_pair("\"", "\""); + add_auto_brace_completion_pair("\'", "\'"); + + /* Delimiter traking */ + add_string_delimiter("\"", "\"", false); + add_string_delimiter("\'", "\'", false); + /* Text Direction */ set_layout_direction(LAYOUT_DIRECTION_LTR); set_text_direction(TEXT_DIRECTION_LTR); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 25b518402b..72fdc6e787 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -66,6 +66,19 @@ private: void _new_line(bool p_split_current_line = true, bool p_above = false); + /* Auto brace completion */ + bool auto_brace_completion_enabled = false; + + /* BracePair open_key must be uniquie and ordered by length. */ + struct BracePair { + String open_key = ""; + String close_key = ""; + }; + Vector<BracePair> auto_brace_completion_pairs; + + int _get_auto_brace_pair_open_at_pos(int p_line, int p_col); + int _get_auto_brace_pair_close_at_pos(int p_line, int p_col); + /* Main Gutter */ enum MainGutterType { MAIN_GUTTER_BREAKPOINT = 0x01, @@ -112,7 +125,7 @@ private: void _update_gutter_indexes(); /* Line Folding */ - bool line_folding_enabled = true; + bool line_folding_enabled = false; /* Delimiters */ enum DelimiterType { @@ -210,6 +223,16 @@ private: void _lines_edited_from(int p_from_line, int p_to_line); + /* Line length guidelines */ + TypedArray<int> line_length_guideline_columns; + Color line_length_guideline_color; + + /* Symbol lookup */ + bool symbol_lookup_on_click_enabled = false; + + String symbol_lookup_new_word = ""; + String symbol_lookup_word = ""; + protected: void _gui_input(const Ref<InputEvent> &p_gui_input) override; void _notification(int p_what); @@ -217,7 +240,9 @@ protected: static void _bind_methods(); public: + /* General overrides */ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + virtual void handle_unicode_input(uint32_t p_unicode) override; /* Indent management */ void set_indent_size(const int p_size); @@ -240,6 +265,22 @@ public: virtual void backspace() override; + /* Auto brace completion */ + void set_auto_brace_completion_enabled(bool p_enabled); + bool is_auto_brace_completion_enabled() const; + + void set_highlight_matching_braces_enabled(bool p_enabled); + bool is_highlight_matching_braces_enabled() const; + + void add_auto_brace_completion_pair(const String &p_open_key, const String &p_close_key); + void set_auto_brace_completion_pairs(const Dictionary &p_auto_brace_completion_pairs); + Dictionary get_auto_brace_completion_pairs() const; + + bool has_auto_brace_completion_open_key(const String &p_open_key) const; + bool has_auto_brace_completion_close_key(const String &p_close_key) const; + + String get_auto_brace_completion_close_key(const String &p_open_key) const; + /* Main Gutter */ void set_draw_breakpoints_gutter(bool p_draw); bool is_drawing_breakpoints_gutter() const; @@ -347,6 +388,18 @@ public: void confirm_code_completion(bool p_replace = false); void cancel_code_completion(); + /* Line length guidelines */ + void set_line_length_guidelines(TypedArray<int> p_guideline_columns); + TypedArray<int> get_line_length_guidelines() const; + + /* Symbol lookup */ + void set_symbol_lookup_on_click_enabled(bool p_enabled); + bool is_symbol_lookup_on_click_enabled() const; + + String get_text_for_symbol_lookup(); + + void set_symbol_lookup_word_as_valid(bool p_valid); + CodeEdit(); ~CodeEdit(); }; diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 83ecf1d534..706d3b48c2 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -49,9 +49,6 @@ GradientEdit::GradientEdit() { popup->add_child(picker); add_child(popup); - - checker = Ref<ImageTexture>(memnew(ImageTexture)); - Ref<Image> img = memnew(Image(checker_bg_png)); } int GradientEdit::_get_point_from_pos(int x) { @@ -311,7 +308,7 @@ void GradientEdit::_notification(int p_what) { int total_w = get_size().width - get_size().height - SPACING; //Draw checker pattern for ramp - _draw_checker(0, 0, total_w, h); + draw_texture_rect(get_theme_icon("GuiMiniCheckerboard", "EditorIcons"), Rect2(0, 0, total_w, h), true); //Draw color ramp Gradient::Point prev; @@ -378,7 +375,7 @@ void GradientEdit::_notification(int p_what) { } //Draw "button" for color selector - _draw_checker(total_w + SPACING, 0, h, h); + draw_texture_rect(get_theme_icon("GuiMiniCheckerboard", "EditorIcons"), Rect2(total_w + SPACING, 0, h, h), true); if (grabbed != -1) { //Draw with selection color draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color); @@ -405,27 +402,6 @@ void GradientEdit::_notification(int p_what) { } } -void GradientEdit::_draw_checker(int x, int y, int w, int h) { - //Draw it with polygon to insert UVs for scale - Vector<Vector2> backPoints; - backPoints.push_back(Vector2(x, y)); - backPoints.push_back(Vector2(x, y + h)); - backPoints.push_back(Vector2(x + w, y + h)); - backPoints.push_back(Vector2(x + w, y)); - Vector<Color> colorPoints; - colorPoints.push_back(Color(1, 1, 1, 1)); - colorPoints.push_back(Color(1, 1, 1, 1)); - colorPoints.push_back(Color(1, 1, 1, 1)); - colorPoints.push_back(Color(1, 1, 1, 1)); - Vector<Vector2> uvPoints; - //Draw checker pattern pixel-perfect and scale it by 2. - uvPoints.push_back(Vector2(x, y)); - uvPoints.push_back(Vector2(x, y + h * .5f / checker->get_height())); - uvPoints.push_back(Vector2(x + w * .5f / checker->get_width(), y + h * .5f / checker->get_height())); - uvPoints.push_back(Vector2(x + w * .5f / checker->get_width(), y)); - draw_polygon(backPoints, colorPoints, uvPoints, checker); -} - Size2 GradientEdit::get_minimum_size() const { return Vector2(0, 16); } diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h index eb7367d598..3d83a02429 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -42,8 +42,6 @@ class GradientEdit : public Control { PopupPanel *popup; ColorPicker *picker; - Ref<ImageTexture> checker; - bool grabbing = false; int grabbed = -1; Vector<Gradient::Point> points; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index fdf6181f1d..258d65112a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -245,6 +245,7 @@ void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_colo ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].custom_bg = p_custom_bg_color; + update(); } Color ItemList::get_item_custom_bg_color(int p_idx) const { @@ -257,6 +258,7 @@ void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_colo ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].custom_fg = p_custom_fg_color; + update(); } Color ItemList::get_item_custom_fg_color(int p_idx) const { diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 490548fce0..44f7200cd7 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -74,7 +74,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { size.width += items[i].text_buf->get_size().x; size.height += vseparation; - if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) { + if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { int accel_w = hseparation * 2; accel_w += items[i].accel_text_buf->get_size().x; accel_max_w = MAX(accel_w, accel_max_w); @@ -635,7 +635,7 @@ void PopupMenu::_draw_items() { } // Accelerator / Shortcut - if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) { + if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->has_valid_event())) { if (rtl) { item_ofs.x = scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding; } else { @@ -1301,7 +1301,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo continue; } - if (items[i].shortcut.is_valid() && items[i].shortcut->is_shortcut(p_event) && (items[i].shortcut_is_global || !p_for_global_only)) { + if (items[i].shortcut.is_valid() && items[i].shortcut->matches_event(p_event) && (items[i].shortcut_is_global || !p_for_global_only)) { activate_item(i); return true; } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3925e0c38e..3cb1f7d2bc 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2289,7 +2289,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub } } -void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, VAlign p_align) { +void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) { if (current->type == ITEM_TABLE) { return; } @@ -2534,7 +2534,7 @@ void RichTextLabel::push_meta(const Variant &p_meta) { _add_item(item, true); } -void RichTextLabel::push_table(int p_columns, VAlign p_align) { +void RichTextLabel::push_table(int p_columns, InlineAlign p_align) { ERR_FAIL_COND(p_columns < 1); ItemTable *item = memnew(ItemTable); @@ -2897,18 +2897,35 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { columns = 1; } - VAlign align = VALIGN_TOP; - if (subtag.size() > 1) { + int align = INLINE_ALIGN_TOP; + if (subtag.size() > 2) { if (subtag[1] == "top" || subtag[1] == "t") { - align = VALIGN_TOP; + align = INLINE_ALIGN_TOP_TO; } else if (subtag[1] == "center" || subtag[1] == "c") { - align = VALIGN_CENTER; + align = INLINE_ALIGN_CENTER_TO; } else if (subtag[1] == "bottom" || subtag[1] == "b") { - align = VALIGN_BOTTOM; + align = INLINE_ALIGN_BOTTOM_TO; + } + if (subtag[2] == "top" || subtag[2] == "t") { + align |= INLINE_ALIGN_TO_TOP; + } else if (subtag[2] == "center" || subtag[2] == "c") { + align |= INLINE_ALIGN_TO_CENTER; + } else if (subtag[2] == "baseline" || subtag[2] == "l") { + align |= INLINE_ALIGN_TO_BASELINE; + } else if (subtag[2] == "bottom" || subtag[2] == "b") { + align |= INLINE_ALIGN_TO_BOTTOM; + } + } else if (subtag.size() > 1) { + if (subtag[1] == "top" || subtag[1] == "t") { + align = INLINE_ALIGN_TOP; + } else if (subtag[1] == "center" || subtag[1] == "c") { + align = INLINE_ALIGN_CENTER; + } else if (subtag[1] == "bottom" || subtag[1] == "b") { + align = INLINE_ALIGN_BOTTOM; } } - push_table(columns, align); + push_table(columns, (InlineAlign)align); pos = brk_end + 1; tag_stack.push_front("table"); } else if (tag == "cell") { @@ -3187,15 +3204,34 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = end; tag_stack.push_front(bbcode_name); } else if (tag.begins_with("img")) { - VAlign align = VALIGN_TOP; + int align = INLINE_ALIGN_CENTER; if (tag.begins_with("img=")) { - String al = tag.substr(4, tag.length()); - if (al == "top" || al == "t") { - align = VALIGN_TOP; - } else if (al == "center" || al == "c") { - align = VALIGN_CENTER; - } else if (al == "bottom" || al == "b") { - align = VALIGN_BOTTOM; + Vector<String> subtag = tag.substr(4, tag.length()).split(","); + if (subtag.size() > 1) { + if (subtag[0] == "top" || subtag[0] == "t") { + align = INLINE_ALIGN_TOP_TO; + } else if (subtag[0] == "center" || subtag[0] == "c") { + align = INLINE_ALIGN_CENTER_TO; + } else if (subtag[0] == "bottom" || subtag[0] == "b") { + align = INLINE_ALIGN_BOTTOM_TO; + } + if (subtag[1] == "top" || subtag[1] == "t") { + align |= INLINE_ALIGN_TO_TOP; + } else if (subtag[1] == "center" || subtag[1] == "c") { + align |= INLINE_ALIGN_TO_CENTER; + } else if (subtag[1] == "baseline" || subtag[1] == "l") { + align |= INLINE_ALIGN_TO_BASELINE; + } else if (subtag[1] == "bottom" || subtag[1] == "b") { + align |= INLINE_ALIGN_TO_BOTTOM; + } + } else if (subtag.size() > 0) { + if (subtag[0] == "top" || subtag[0] == "t") { + align = INLINE_ALIGN_TOP; + } else if (subtag[0] == "center" || subtag[0] == "c") { + align = INLINE_ALIGN_CENTER; + } else if (subtag[0] == "bottom" || subtag[0] == "b") { + align = INLINE_ALIGN_BOTTOM; + } } } @@ -3236,7 +3272,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } } - add_image(texture, width, height, color, align); + add_image(texture, width, height, color, (InlineAlign)align); } pos = end; @@ -3961,7 +3997,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); - ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); @@ -3981,7 +4017,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); - ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP)); + ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP)); ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0))); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 999d8b05fd..a73176ba7e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -161,7 +161,7 @@ private: struct ItemImage : public Item { Ref<Texture2D> image; - VAlign inline_align = VALIGN_TOP; + InlineAlign inline_align = INLINE_ALIGN_CENTER; Size2 size; Color color; ItemImage() { type = ITEM_IMAGE; } @@ -248,7 +248,7 @@ private: int total_width = 0; int total_height = 0; - VAlign inline_align = VALIGN_TOP; + InlineAlign inline_align = INLINE_ALIGN_TOP; ItemTable() { type = ITEM_TABLE; } }; @@ -463,7 +463,7 @@ private: public: String get_text(); void add_text(const String &p_text); - void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP); + void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlign p_align = INLINE_ALIGN_CENTER); void add_newline(); bool remove_line(const int p_line); void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); @@ -484,7 +484,7 @@ public: void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize); void push_meta(const Variant &p_meta); - void push_table(int p_columns, VAlign p_align = VALIGN_TOP); + void push_table(int p_columns, InlineAlign p_align = INLINE_ALIGN_TOP); void push_fade(int p_start_index, int p_length); void push_shake(int p_strength, float p_rate); void push_wave(float p_frequency, float p_amplitude); diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp index 962c6dcc60..885a51e058 100644 --- a/scene/gui/shortcut.cpp +++ b/scene/gui/shortcut.cpp @@ -32,42 +32,39 @@ #include "core/os/keyboard.h" -void Shortcut::set_shortcut(const Ref<InputEvent> &p_shortcut) { - shortcut = p_shortcut; +void Shortcut::set_event(const Ref<InputEvent> &p_event) { + event = p_event; emit_changed(); } -Ref<InputEvent> Shortcut::get_shortcut() const { - return shortcut; +Ref<InputEvent> Shortcut::get_event() const { + return event; } -bool Shortcut::is_shortcut(const Ref<InputEvent> &p_event) const { - return shortcut.is_valid() && shortcut->is_match(p_event, true); +bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { + return event.is_valid() && event->is_match(p_event, true); } String Shortcut::get_as_text() const { - if (shortcut.is_valid()) { - return shortcut->as_text(); + if (event.is_valid()) { + return event->as_text(); } else { return "None"; } } -bool Shortcut::is_valid() const { - return shortcut.is_valid(); +bool Shortcut::has_valid_event() const { + return event.is_valid(); } void Shortcut::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_shortcut", "event"), &Shortcut::set_shortcut); - ClassDB::bind_method(D_METHOD("get_shortcut"), &Shortcut::get_shortcut); + ClassDB::bind_method(D_METHOD("set_event", "event"), &Shortcut::set_event); + ClassDB::bind_method(D_METHOD("get_event"), &Shortcut::get_event); - ClassDB::bind_method(D_METHOD("is_valid"), &Shortcut::is_valid); + ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event); - ClassDB::bind_method(D_METHOD("is_shortcut", "event"), &Shortcut::is_shortcut); + ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event); ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_shortcut", "get_shortcut"); -} - -Shortcut::Shortcut() { + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_event", "get_event"); } diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h index ea91f29b5d..249dd1971f 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -37,20 +37,18 @@ class Shortcut : public Resource { GDCLASS(Shortcut, Resource); - Ref<InputEvent> shortcut; + Ref<InputEvent> event; protected: static void _bind_methods(); public: - void set_shortcut(const Ref<InputEvent> &p_shortcut); - Ref<InputEvent> get_shortcut() const; - bool is_shortcut(const Ref<InputEvent> &p_event) const; - bool is_valid() const; + void set_event(const Ref<InputEvent> &p_shortcut); + Ref<InputEvent> get_event() const; + bool matches_event(const Ref<InputEvent> &p_event) const; + bool has_valid_event() const; String get_as_text() const; - - Shortcut(); }; #endif // SHORTCUT_H diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 4b8c4b3e16..cc41d961f6 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -899,7 +899,7 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { if (hover_now < 0) { hover_now = get_tab_count() - 1; } - move_child(get_tab_control(tab_from_id), hover_now); + move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index()); set_current_tab(hover_now); } else if (get_tabs_rearrange_group() != -1) { // drag and drop between TabContainers @@ -912,7 +912,7 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) { if (hover_now < 0) { hover_now = get_tab_count() - 1; } - move_child(moving_tabc, hover_now); + move_child(moving_tabc, get_tab_control(hover_now)->get_index()); set_current_tab(hover_now); emit_signal(SNAME("tab_changed"), hover_now); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index be3edccc99..1b3935dd25 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -63,45 +63,6 @@ static bool _is_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } -static bool _is_pair_right_symbol(char32_t c) { - return c == '"' || - c == '\'' || - c == ')' || - c == ']' || - c == '}'; -} - -static bool _is_pair_left_symbol(char32_t c) { - return c == '"' || - c == '\'' || - c == '(' || - c == '[' || - c == '{'; -} - -static bool _is_pair_symbol(char32_t c) { - return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); -} - -static char32_t _get_right_pair_symbol(char32_t c) { - if (c == '"') { - return '"'; - } - if (c == '\'') { - return '\''; - } - if (c == '(') { - return ')'; - } - if (c == '[') { - return ']'; - } - if (c == '{') { - return '}'; - } - return 0; -} - /////////////////////////////////////////////////////////////////////////////// void TextEdit::Text::set_font(const Ref<Font> &p_font) { @@ -633,29 +594,6 @@ void TextEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color); } - if (line_length_guidelines) { - const int hard_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_hard_col - cursor.x_ofs; - if (hard_x > xmargin_beg && hard_x < xmargin_end) { - if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - hard_x, 0), Point2(size.width - hard_x, size.height), cache.line_length_guideline_color); - } else { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(hard_x, 0), Point2(hard_x, size.height), cache.line_length_guideline_color); - } - } - - // Draw a "Soft" line length guideline, less visible than the hard line length guideline. - // It's usually set to a lower column compared to the hard line length guideline. - // Only drawn if its column differs from the hard line length guideline. - const int soft_x = xmargin_beg + (int)cache.font->get_char_size('0', 0, cache.font_size).width * line_length_guideline_soft_col - cursor.x_ofs; - if (hard_x != soft_x && soft_x > xmargin_beg && soft_x < xmargin_end) { - if (rtl) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - soft_x, 0), Point2(size.width - soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5)); - } else { - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(soft_x, 0), Point2(soft_x, size.height), cache.line_length_guideline_color * Color(1, 1, 1, 0.5)); - } - } - } - int brace_open_match_line = -1; int brace_open_match_column = -1; bool brace_open_matching = false; @@ -665,7 +603,7 @@ void TextEdit::_notification(int p_what) { bool brace_close_matching = false; bool brace_close_mismatch = false; - if (brace_matching_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) { + if (highlight_matching_braces_enabled && cursor.line >= 0 && cursor.line < text.size() && cursor.column >= 0) { if (cursor.column < text[cursor.line].length()) { // Check for open. char32_t c = text[cursor.line][cursor.column]; @@ -1239,11 +1177,11 @@ void TextEdit::_notification(int p_what) { } } - if (!clipped && select_identifiers_enabled && highlighted_word.length() != 0) { // Highlight word - if (_is_char(highlighted_word[0]) || highlighted_word[0] == '.') { - int highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); + if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word + if (_is_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') { + int highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0); while (highlighted_word_col != -1) { - Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + highlighted_word.length() + start); + Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + lookup_symbol_word.length() + start); for (int j = 0; j < sel.size(); j++) { Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height); if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) { @@ -1260,7 +1198,7 @@ void TextEdit::_notification(int p_what) { draw_rect(rect, cache.font_selected_color); } - highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); + highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1); } } } @@ -1308,7 +1246,7 @@ void TextEdit::_notification(int p_what) { int char_pos = char_ofs + char_margin + ofs_x; if (char_pos >= xmargin_beg) { - if (brace_matching_enabled) { + if (highlight_matching_braces_enabled) { if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) || (cursor.column == glyphs[j].start && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) { if (brace_open_mismatch) { @@ -1567,130 +1505,6 @@ void TextEdit::_notification(int p_what) { } } -void TextEdit::_consume_pair_symbol(char32_t ch) { - int cursor_position_to_move = cursor_get_column() + 1; - - char32_t ch_single[2] = { ch, 0 }; - char32_t ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 }; - char32_t ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 }; - - if (is_selection_active()) { - int new_column, new_line; - - begin_complex_operation(); - _insert_text(get_selection_from_line(), get_selection_from_column(), - ch_single, - &new_line, &new_column); - - int to_col_offset = 0; - if (get_selection_from_line() == get_selection_to_line()) { - to_col_offset = 1; - } - - _insert_text(get_selection_to_line(), - get_selection_to_column() + to_col_offset, - ch_single_pair, - &new_line, &new_column); - end_complex_operation(); - - cursor_set_line(get_selection_to_line()); - cursor_set_column(get_selection_to_column() + to_col_offset); - - deselect(); - update(); - return; - } - - if ((ch == '\'' || ch == '"') && - cursor_get_column() > 0 && _is_text_char(text[cursor.line][cursor_get_column() - 1]) && !_is_pair_right_symbol(text[cursor.line][cursor_get_column()])) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - - if (cursor_get_column() < text[cursor.line].length()) { - if (_is_text_char(text[cursor.line][cursor_get_column()])) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - if (_is_pair_right_symbol(ch) && - text[cursor.line][cursor_get_column()] == ch) { - cursor_set_column(cursor_position_to_move); - return; - } - } - - String line = text[cursor.line]; - - bool in_single_quote = false; - bool in_double_quote = false; - bool found_comment = false; - - int c = 0; - while (c < line.length()) { - if (line[c] == '\\') { - c++; // Skip quoted anything. - - if (cursor.column == c) { - break; - } - } else if (!in_single_quote && !in_double_quote && line[c] == '#') { - found_comment = true; - break; - } else { - if (line[c] == '\'' && !in_double_quote) { - in_single_quote = !in_single_quote; - } else if (line[c] == '"' && !in_single_quote) { - in_double_quote = !in_double_quote; - } - } - - c++; - - if (cursor.column == c) { - break; - } - } - - // Do not need to duplicate quotes while in comments - if (found_comment) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - - return; - } - - // Disallow inserting duplicated quotes while already in string - if ((in_single_quote || in_double_quote) && (ch == '"' || ch == '\'')) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - - return; - } - - insert_text_at_cursor(ch_pair); - cursor_set_column(cursor_position_to_move); -} - -void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) { - bool remove_right_symbol = false; - - if (cursor.column < text[cursor.line].length() && cursor.column > 0) { - char32_t left_char = text[cursor.line][cursor.column - 1]; - char32_t right_char = text[cursor.line][cursor.column]; - - if (right_char == _get_right_pair_symbol(left_char)) { - remove_right_symbol = true; - } - } - if (remove_right_symbol) { - _remove_text(prev_line, prev_column, cursor.line, cursor.column + 1); - } else { - _remove_text(prev_line, prev_column, cursor.line, cursor.column); - } -} - void TextEdit::backspace() { ScriptInstance *si = get_script_instance(); if (si && si->has_method("_backspace")) { @@ -1719,14 +1533,7 @@ void TextEdit::backspace() { if (is_line_hidden(cursor.line)) { set_line_as_hidden(prev_line, true); } - - if (auto_brace_completion_enabled && - cursor.column > 0 && - _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { - _consume_backspace_for_pair_symbol(prev_line, prev_column); - } else { - _remove_text(prev_line, prev_column, cursor.line, cursor.column); - } + _remove_text(prev_line, prev_column, cursor.line, cursor.column); cursor_set_line(prev_line, false, true); cursor_set_column(prev_column); @@ -2101,6 +1908,7 @@ void TextEdit::delete_selection() { } selection.active = false; + selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); cursor_set_line(selection.from_line, false, false); cursor_set_column(selection.from_column); @@ -2137,13 +1945,21 @@ void TextEdit::_move_cursor_document_end(bool p_select) { } } -void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection) { - if (p_had_selection) { +void TextEdit::handle_unicode_input(uint32_t p_unicode) { + ScriptInstance *si = get_script_instance(); + if (si && si->has_method("_handle_unicode_input")) { + si->call("_handle_unicode_input", p_unicode); + return; + } + + bool had_selection = selection.active; + if (had_selection) { + begin_complex_operation(); delete_selection(); } // Remove the old character if in insert mode and no selection. - if (insert_mode && !p_had_selection) { + if (insert_mode && !had_selection) { begin_complex_operation(); // Make sure we don't try and remove empty space. @@ -2152,15 +1968,10 @@ void TextEdit::_handle_unicode_character(uint32_t unicode, bool p_had_selection) } } - const char32_t chr[2] = { (char32_t)unicode, 0 }; + const char32_t chr[2] = { (char32_t)p_unicode, 0 }; + insert_text_at_cursor(chr); - if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } - - if ((insert_mode && !p_had_selection) || (selection.active != p_had_selection)) { + if ((insert_mode && !had_selection) || (had_selection)) { end_complex_operation(); } } @@ -2301,6 +2112,10 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const r_row = row; } +bool TextEdit::is_dragging_cursor() const { + return dragging_selection || dragging_minimap; +} + void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -2478,14 +2293,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } else { if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { - if (mb->is_command_pressed() && highlighted_word != String()) { - int row, col; - _get_mouse_pos(Point2i(mpos.x, mpos.y), row, col); - - emit_signal(SNAME("symbol_lookup"), highlighted_word, row, col); - return; - } - dragging_minimap = false; dragging_selection = false; can_drag_minimap = false; @@ -2520,18 +2327,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (is_layout_rtl()) { mpos.x = get_size().x - mpos.x; } - if (select_identifiers_enabled) { - if (!dragging_minimap && !dragging_selection && mm->is_command_pressed() && mm->get_button_mask() == 0) { - String new_word = get_word_at_pos(mpos); - if (new_word != highlighted_word) { - emit_signal(SNAME("symbol_validate"), new_word); - } - } else { - if (highlighted_word != String()) { - set_highlighted_word(String()); - } - } - } if (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { // Ignore if dragging. _reset_caret_blink_timer(); @@ -2566,23 +2361,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventKey> k = p_gui_input; if (k.is_valid()) { - // Ctrl + Hover symbols -#ifdef OSX_ENABLED - if (k->get_keycode() == KEY_META) { -#else - if (k->get_keycode() == KEY_CTRL) { -#endif - if (select_identifiers_enabled) { - if (k->is_pressed() && !dragging_minimap && !dragging_selection) { - Point2 mp = _get_local_mouse_pos(); - emit_signal(SNAME("symbol_validate"), get_word_at_pos(mp)); - } else { - set_highlighted_word(String()); - } - } - return; - } - if (!k->is_pressed()) { return; } @@ -2598,9 +2376,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { // * No Modifiers are pressed (except shift) bool allow_unicode_handling = !(k->is_command_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed()); - // Save here for insert mode, just in case it is cleared in the following section. - bool had_selection = selection.active; - selection.selecting_text = false; // Check and handle all built in shortcuts. @@ -2806,9 +2581,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } + // Handle Unicode (if no modifiers active). if (allow_unicode_handling && !readonly && k->get_unicode() >= 32) { - // Handle Unicode (if no modifiers active). - _handle_unicode_character(k->get_unicode(), had_selection); + handle_unicode_input(k->get_unicode()); accept_event(); return; } @@ -3151,16 +2926,6 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i current_op = op; } -void TextEdit::_insert_text_at_cursor(const String &p_text) { - int new_column, new_line; - _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column); - _update_scrollbars(); - cursor_set_line(new_line, false); - cursor_set_column(new_column); - - update(); -} - int TextEdit::get_char_count() { int totalsize = 0; @@ -3704,23 +3469,19 @@ int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const { void TextEdit::insert_text_at_cursor(const String &p_text) { if (selection.active) { - cursor_set_line(selection.from_line, false); - cursor_set_column(selection.from_column); - - _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - selection.active = false; - selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; + delete_selection(); } - _insert_text_at_cursor(p_text); + int new_column, new_line; + _insert_text(cursor.line, cursor.column, p_text, &new_line, &new_column); + _update_scrollbars(); + + cursor_set_line(new_line, false); + cursor_set_column(new_column); update(); } Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { - if (highlighted_word != String()) { - return CURSOR_POINTING_HAND; - } - int row, col; _get_mouse_pos(p_pos, row, col); @@ -3753,7 +3514,7 @@ void TextEdit::set_text(String p_text) { setting_text = true; if (!undo_enabled) { _clear(); - _insert_text_at_cursor(p_text); + insert_text_at_cursor(p_text); } if (undo_enabled) { @@ -3762,7 +3523,7 @@ void TextEdit::set_text(String p_text) { begin_complex_operation(); _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0)); - _insert_text_at_cursor(p_text); + insert_text_at_cursor(p_text); end_complex_operation(); selection.active = false; } @@ -3903,30 +3664,6 @@ bool TextEdit::get_draw_control_chars() const { return draw_control_chars; } -String TextEdit::get_text_for_lookup_completion() { - int row, col; - Point2i mp = _get_local_mouse_pos(); - _get_mouse_pos(mp, row, col); - - String longthing; - int len = text.size(); - for (int i = 0; i < len; i++) { - if (i == row) { - longthing += text[i].substr(0, col); - longthing += String::chr(0xFFFF); // Not unicode, represents the cursor. - longthing += text[i].substr(col, text[i].size()); - } else { - longthing += text[i]; - } - - if (i != len - 1) { - longthing += "\n"; - } - } - - return longthing; -} - String TextEdit::get_line(int line) const { if (line < 0 || line >= text.size()) { return ""; @@ -4015,9 +3752,8 @@ void TextEdit::_update_caches() { cache.font_readonly_color = get_theme_color(SNAME("font_readonly_color")); cache.selection_color = get_theme_color(SNAME("selection_color")); cache.current_line_color = get_theme_color(SNAME("current_line_color")); - cache.line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color")); cache.code_folding_color = get_theme_color(SNAME("code_folding_color"), SNAME("CodeEdit")); - cache.brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color")); + cache.brace_mismatch_color = get_theme_color(SNAME("brace_mismatch_color"), SNAME("CodeEdit")); cache.word_highlighted_color = get_theme_color(SNAME("word_highlighted_color")); cache.search_result_color = get_theme_color(SNAME("search_result_color")); cache.search_result_border_color = get_theme_color(SNAME("search_result_border_color")); @@ -4364,7 +4100,7 @@ void TextEdit::paste() { clipboard += ins; } - _insert_text_at_cursor(clipboard); + insert_text_at_cursor(clipboard); end_complex_operation(); update(); @@ -5324,21 +5060,6 @@ void TextEdit::insert_at(const String &p_text, int at) { } } -void TextEdit::set_show_line_length_guidelines(bool p_show) { - line_length_guidelines = p_show; - update(); -} - -void TextEdit::set_line_length_guideline_soft_column(int p_column) { - line_length_guideline_soft_col = p_column; - update(); -} - -void TextEdit::set_line_length_guideline_hard_column(int p_column) { - line_length_guideline_hard_col = p_column; - update(); -} - void TextEdit::set_draw_minimap(bool p_draw) { if (draw_minimap != p_draw) { draw_minimap = p_draw; @@ -5515,19 +5236,11 @@ void TextEdit::menu_option(int p_option) { } } -void TextEdit::set_highlighted_word(const String &new_word) { - highlighted_word = new_word; +void TextEdit::_set_symbol_lookup_word(const String &p_symbol) { + lookup_symbol_word = p_symbol; update(); } -void TextEdit::set_select_identifiers_on_hover(bool p_enable) { - select_identifiers_enabled = p_enable; -} - -bool TextEdit::is_selecting_identifiers_on_hover_enabled() const { - return select_identifiers_enabled; -} - void TextEdit::set_context_menu_enabled(bool p_enable) { context_menu_enabled = p_enable; } @@ -5719,6 +5432,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("delete_selection"), &TextEdit::delete_selection); ClassDB::bind_method(D_METHOD("backspace"), &TextEdit::backspace); BIND_VMETHOD(MethodInfo("_backspace")); + BIND_VMETHOD(MethodInfo("_handle_unicode_input", PropertyInfo(Variant::INT, "unicode"))) ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); @@ -5727,6 +5441,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column"), &TextEdit::select); ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &TextEdit::deselect); + ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor); ClassDB::bind_method(D_METHOD("is_selection_active"), &TextEdit::is_selection_active); ClassDB::bind_method(D_METHOD("get_selection_from_line"), &TextEdit::get_selection_from_line); @@ -5777,6 +5492,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable); ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters); ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw); + ClassDB::bind_method(D_METHOD("get_total_gutter_width"), &TextEdit::get_total_gutter_width); // Line gutters. ClassDB::bind_method(D_METHOD("set_line_gutter_metadata", "line", "gutter", "metadata"), &TextEdit::set_line_gutter_metadata); @@ -5858,8 +5574,6 @@ void TextEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("gutter_clicked", PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "gutter"))); ADD_SIGNAL(MethodInfo("gutter_added")); ADD_SIGNAL(MethodInfo("gutter_removed")); - ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column"))); - ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol"))); BIND_ENUM_CONSTANT(MENU_CUT); BIND_ENUM_CONSTANT(MENU_COPY); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 62d576b48a..9e6dedb267 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -280,9 +280,6 @@ private: bool cursor_changed_dirty = false; bool text_changed_dirty = false; bool undo_enabled = true; - bool line_length_guidelines = false; - int line_length_guideline_soft_col = 80; - int line_length_guideline_hard_col = 100; bool hiding_enabled = false; bool draw_minimap = false; int minimap_width = 80; @@ -291,7 +288,6 @@ private: bool highlight_all_occurrences = false; bool scroll_past_end_of_file_enabled = false; - bool brace_matching_enabled = false; bool highlight_current_line = false; String cut_copy_line; @@ -309,7 +305,7 @@ private: float target_v_scroll = 0.0; float v_scroll_speed = 80.0; - String highlighted_word; + String lookup_symbol_word; uint64_t last_dblclk = 0; @@ -386,7 +382,6 @@ private: Size2 get_minimum_size() const override; int _get_control_height() const; - Point2 _get_local_mouse_pos() const; int _get_menu_action_accelerator(const String &p_action); void _reset_caret_blink_timer(); @@ -431,10 +426,9 @@ private: void _delete(bool p_word = false, bool p_all_to_right = false); void _move_cursor_document_start(bool p_select); void _move_cursor_document_end(bool p_select); - void _handle_unicode_character(uint32_t unicode, bool p_had_selection); protected: - bool auto_brace_completion_enabled = false; + bool highlight_matching_braces_enabled = false; struct Cache { Ref<Texture2D> tab_icon; @@ -455,7 +449,6 @@ protected: Color selection_color; Color code_folding_color; Color current_line_color; - Color line_length_guideline_color; Color brace_mismatch_color; Color word_highlighted_color; Color search_result_color; @@ -470,19 +463,17 @@ protected: void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr); void _remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column); - void _insert_text_at_cursor(const String &p_text); virtual void _gui_input(const Ref<InputEvent> &p_gui_input); void _notification(int p_what); - void _consume_pair_symbol(char32_t ch); - void _consume_backspace_for_pair_symbol(int prev_line, int prev_column); - static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + void _set_symbol_lookup_word(const String &p_symbol); + public: /* Syntax Highlighting. */ Ref<SyntaxHighlighter> get_syntax_highlighter(); @@ -577,8 +568,10 @@ public: virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; + Point2 _get_local_mouse_pos() const; void _get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) const; void _get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const; + bool is_dragging_cursor() const; //void delete_char(); //void delete_line(); @@ -607,7 +600,6 @@ public: void set_structured_text_bidi_override_options(Array p_args); Array get_structured_text_bidi_override_options() const; - void set_highlighted_word(const String &new_word); void set_text(String p_text); void insert_text_at_cursor(const String &p_text); void insert_at(const String &p_text, int at); @@ -635,13 +627,6 @@ public: scroll_past_end_of_file_enabled = p_enabled; update(); } - inline void set_auto_brace_completion(bool p_enabled) { - auto_brace_completion_enabled = p_enabled; - } - inline void set_brace_matching(bool p_enabled) { - brace_matching_enabled = p_enabled; - update(); - } void center_viewport_to_cursor(); @@ -686,6 +671,7 @@ public: void delete_selection(); + virtual void handle_unicode_input(uint32_t p_unicode); virtual void backspace(); void cut(); void copy(); @@ -751,10 +737,6 @@ public: void set_highlight_current_line(bool p_enabled); bool is_highlight_current_line_enabled() const; - void set_show_line_length_guidelines(bool p_show); - void set_line_length_guideline_soft_column(int p_column); - void set_line_length_guideline_hard_column(int p_column); - void set_draw_minimap(bool p_draw); bool is_drawing_minimap() const; @@ -766,9 +748,6 @@ public: void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); - void set_select_identifiers_on_hover(bool p_enable); - bool is_selecting_identifiers_on_hover_enabled() const; - void set_context_menu_enabled(bool p_enable); bool is_context_menu_enabled(); @@ -784,8 +763,6 @@ public: bool is_menu_visible() const; PopupMenu *get_menu() const; - String get_text_for_lookup_completion(); - virtual bool is_text_field() const override; TextEdit(); ~TextEdit(); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 10e6642303..be711b4c4f 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -462,8 +462,6 @@ private: void _gui_input(Ref<InputEvent> p_event); void _notification(int p_what); - Size2 get_minimum_size() const override; - void item_edited(int p_column, TreeItem *p_item, bool p_lmb = true); void item_changed(int p_column, TreeItem *p_item); void item_selected(int p_column, TreeItem *p_item); @@ -721,6 +719,8 @@ public: void set_allow_reselect(bool p_allow); bool get_allow_reselect() const; + Size2 get_minimum_size() const override; + Tree(); ~Tree(); }; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index f1e5574351..14cd76ab7d 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -116,6 +116,9 @@ void Node::_notification(int p_notification) { memdelete(data.path_cache); data.path_cache = nullptr; } + if (data.filename.length()) { + get_multiplayer()->scene_enter_exit_notify(data.filename, this, false); + } } break; case NOTIFICATION_PATH_CHANGED: { if (data.path_cache) { @@ -147,6 +150,10 @@ void Node::_notification(int p_notification) { get_script_instance()->call(SceneStringNames::get_singleton()->_ready); } + if (data.filename.length()) { + ERR_FAIL_COND(!is_inside_tree()); + get_multiplayer()->scene_enter_exit_notify(data.filename, this, true); + } } break; case NOTIFICATION_POSTINITIALIZE: { diff --git a/scene/main/node.h b/scene/main/node.h index 20315d7a86..7175d4c740 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -67,6 +67,12 @@ public: #endif }; + enum NameCasing { + NAME_CASING_PASCAL_CASE, + NAME_CASING_CAMEL_CASE, + NAME_CASING_SNAKE_CASE + }; + struct Comparator { bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); } }; @@ -140,12 +146,6 @@ private: } data; - enum NameCasing { - NAME_CASING_PASCAL_CASE, - NAME_CASING_CAMEL_CASE, - NAME_CASING_SNAKE_CASE - }; - Ref<MultiplayerAPI> multiplayer; void _print_tree_pretty(const String &prefix, const bool last); @@ -202,6 +202,7 @@ protected: static String _get_name_num_separator(); friend class SceneState; + friend class MultiplayerAPI; void _add_child_nocheck(Node *p_child, const StringName &p_name); void _set_owner_nocheck(Node *p_owner); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8ede8e9a0b..4d3a4ea334 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -1000,9 +1000,12 @@ void register_scene_types() { for (int i = 0; i < 20; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); + } + + for (int i = 0; i < 32; i++) { GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i), ""); GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i), ""); } diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 8ffd2df112..ef070589e4 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -261,11 +261,11 @@ void AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, in sign = -1; } - float global_rate_scale = AudioServer::get_singleton()->get_global_rate_scale(); - float base_rate = AudioServer::get_singleton()->get_mix_rate() * global_rate_scale; + float base_rate = AudioServer::get_singleton()->get_mix_rate(); float srate = base->mix_rate; srate *= p_rate_scale; - float fincrement = srate / base_rate; + float playback_speed_scale = AudioServer::get_singleton()->get_playback_speed_scale(); + float fincrement = (srate * playback_speed_scale) / base_rate; int32_t increment = int32_t(MAX(fincrement * MIX_FRAC_LEN, 1)); increment *= sign; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index a4e126fcc6..efc37709a5 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -457,7 +457,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8)); theme->set_color("caret_color", "TextEdit", control_font_color); theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0)); - theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2)); theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15)); theme->set_constant("line_spacing", "TextEdit", 4 * scale); @@ -502,8 +501,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0)); theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2)); theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4)); - theme->set_color("safe_line_number_color", "CodeEdit", Color(0.67, 0.78, 0.67, 0.6)); theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15)); + theme->set_color("line_length_guideline_color", "CodeEdit", Color(0.3, 0.5, 0.8, 0.1)); theme->set_constant("completion_lines", "CodeEdit", 7); theme->set_constant("completion_max_width", "CodeEdit", 50); @@ -864,6 +863,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png)); theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png)); + // ColorPickerButton + theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png)); // TooltipPanel diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index cdb1bc527b..97e64e4746 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -50,10 +50,6 @@ static const unsigned char checked_disabled_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x28, 0x2d, 0xf, 0x53, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x73, 0x72, 0x7b, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x7a, 0x78, 0x83, 0x82, 0x80, 0x8a, 0x90, 0x90, 0x93, 0x6a, 0x69, 0x70, 0x6a, 0x68, 0x70, 0x93, 0x93, 0x95, 0x58, 0x58, 0x5c, 0x58, 0x58, 0x5b, 0x7d, 0x7d, 0x7f, 0x58, 0x58, 0x5b, 0xa4, 0xa4, 0xa4, 0x9e, 0x9e, 0xa0, 0x9e, 0x9e, 0x9e, 0x9b, 0x9b, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x93, 0x93, 0x94, 0x8f, 0x8f, 0x8f, 0x86, 0x86, 0x88, 0x85, 0x85, 0x86, 0x82, 0x82, 0x83, 0x81, 0x81, 0x83, 0x7f, 0x7f, 0x81, 0x7c, 0x7c, 0x7e, 0x7a, 0x7a, 0x7d, 0x78, 0x78, 0x7b, 0x71, 0x71, 0x74, 0x68, 0x68, 0x6c, 0x66, 0x66, 0x6a, 0x65, 0x65, 0x68, 0x63, 0x63, 0x66, 0x5f, 0x5f, 0x63, 0x5c, 0x5c, 0x60, 0x5c, 0x5c, 0x5f, 0x5a, 0x5a, 0x5e, 0x59, 0x59, 0x5d, 0x59, 0x59, 0x5c, 0x58, 0x58, 0x5b, 0x57, 0x57, 0x5a, 0x56, 0x56, 0x59, 0x10, 0x13, 0xbb, 0xf, 0x0, 0x0, 0x0, 0x10, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0x27, 0x27, 0x50, 0x66, 0x68, 0x6a, 0x81, 0xb4, 0xb4, 0xdd, 0xfa, 0xfa, 0xfb, 0xfb, 0x5b, 0xd1, 0xf1, 0xe6, 0x0, 0x0, 0x0, 0x96, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0x5d, 0x8f, 0xc9, 0x12, 0x82, 0x30, 0x14, 0x4, 0x87, 0x18, 0x50, 0x51, 0x44, 0x25, 0x42, 0x4, 0x77, 0xc4, 0x8d, 0x97, 0x0, 0xf9, 0xff, 0x8f, 0xb3, 0x88, 0xa4, 0x4a, 0xed, 0x63, 0x5f, 0xa6, 0x7, 0xf8, 0x7, 0x1e, 0xe3, 0x7e, 0x60, 0x19, 0x4f, 0x46, 0x1e, 0x0, 0x36, 0x8d, 0x4c, 0x67, 0x59, 0x65, 0x33, 0x6, 0x80, 0x47, 0xad, 0x56, 0x3d, 0xb7, 0x3c, 0x5d, 0x70, 0x0, 0xbe, 0xd1, 0x44, 0x65, 0x4d, 0x94, 0xc8, 0xc2, 0xf8, 0x0, 0x82, 0x4e, 0x91, 0x94, 0x15, 0x5d, 0xd2, 0xec, 0xde, 0x5, 0x83, 0x38, 0xc8, 0xe3, 0x63, 0x23, 0xce, 0xca, 0x9, 0x7a, 0x6e, 0xf3, 0x93, 0x48, 0x1a, 0x27, 0x14, 0x35, 0x3b, 0xb9, 0x5e, 0x56, 0xe4, 0x84, 0x22, 0xba, 0xa, 0x51, 0xd0, 0xb7, 0xa8, 0xcb, 0xfd, 0xcb, 0x9, 0x3b, 0xfb, 0x41, 0xdb, 0x59, 0x17, 0xa6, 0x94, 0x6e, 0xe3, 0x3e, 0x8c, 0x85, 0xf1, 0x90, 0x6e, 0xe6, 0x21, 0xfb, 0x39, 0xe7, 0x73, 0xe6, 0xfd, 0x5f, 0x7, 0xde, 0xc3, 0xb5, 0x16, 0x87, 0xb0, 0x9e, 0x42, 0x46, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; -static const unsigned char checker_bg_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0xe1, 0x64, 0xe1, 0x57, 0x0, 0x0, 0x0, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0xc0, 0xd0, 0x0, 0xc4, 0xf8, 0x18, 0xf5, 0x84, 0x19, 0x0, 0x9f, 0x5f, 0xa, 0x1, 0xf8, 0xef, 0x65, 0xf4, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 -}; - static const unsigned char close_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x62, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xa0, 0x16, 0xe0, 0x8c, 0xe0, 0x11, 0x43, 0xe6, 0xf3, 0x88, 0x71, 0x46, 0xa0, 0x48, 0x73, 0xfc, 0xe3, 0xb8, 0xcc, 0x23, 0x86, 0x90, 0xe6, 0xb8, 0xcc, 0xf1, 0xf, 0x49, 0x9, 0x8f, 0x28, 0xe7, 0x25, 0x8e, 0xff, 0x1c, 0xd7, 0xb9, 0x24, 0x91, 0x79, 0xdc, 0x12, 0x40, 0xe, 0xa6, 0x12, 0x54, 0x69, 0x4c, 0x25, 0xb7, 0x38, 0xae, 0x21, 0xa4, 0x31, 0x94, 0x80, 0x24, 0x81, 0xf0, 0x36, 0x48, 0x1a, 0xaf, 0x2, 0x88, 0x5b, 0xf0, 0x5a, 0x81, 0xa1, 0x4, 0xe1, 0x34, 0x84, 0x73, 0xb1, 0x4a, 0xa3, 0x7b, 0x9a, 0x70, 0x40, 0x11, 0xe, 0x6a, 0xca, 0x1, 0x0, 0x2a, 0x28, 0x37, 0x83, 0x3e, 0x27, 0xb0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 8550af8bcb..cab6c0378a 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -449,6 +449,7 @@ bool Environment::is_sdfgi_enabled() const { } void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) { + ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1); sdfgi_cascades = p_cascades; _update_sdfgi(); } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 250a2311a0..2b93414906 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1019,11 +1019,11 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type); wf->store_64(0); //offset to import metadata, this is no longer used - f->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); + wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); - f->store_64(res_uid); + wf->store_64(res_uid); - for (int i = 0; i < 5; i++) { + for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { wf->store_32(0); // reserved } @@ -1073,7 +1073,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) bs_save_unicode_string(wf.f, type); bs_save_unicode_string(wf.f, path); - wf.f->store_64(uid); + wf->store_64(uid); int lindex = dummy_read.external_resources.size(); Ref<DummyResource> dr; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 424a54f344..44d524f142 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -76,8 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { if (default_textures.has(pi.name)) { //do not show default textures continue; } + String original_name = pi.name; pi.name = "shader_param/" + pi.name; - params_cache[pi.name] = pi.name; + params_cache[pi.name] = original_name; if (p_params) { //small little hack if (pi.type == Variant::RID) { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 9f8c35b668..875aa30824 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1120,6 +1120,7 @@ SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const { void SurfaceTool::optimize_indices_for_cache() { ERR_FAIL_COND(optimize_vertex_cache_func == nullptr); ERR_FAIL_COND(index_array.size() == 0); + ERR_FAIL_COND(index_array.size() % 3 != 0); LocalVector old_index_array = index_array; memset(index_array.ptr(), 0, index_array.size() * sizeof(int)); diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index f1eff6e84f..5e60c7c4ed 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -56,8 +56,8 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(VALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width); ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width); @@ -175,13 +175,13 @@ bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_ return res; } -bool TextLine::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); dirty = true; return res; } -bool TextLine::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { const_cast<TextLine *>(this)->_shape(); return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); } diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 1b5c1a3123..b773bd05be 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -76,8 +76,8 @@ public: bool get_preserve_control() const; bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); - bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER); void set_align(HAlign p_align); HAlign get_align() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 958c94fe31..ffae551570 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -59,8 +59,8 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGN_CENTER)); ClassDB::bind_method(D_METHOD("set_align", "align"), &TextParagraph::set_align); ClassDB::bind_method(D_METHOD("get_align"), &TextParagraph::get_align); @@ -290,13 +290,13 @@ void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) { dirty_lines = true; } -bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align, int p_length) { +bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) { bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); dirty_lines = true; return res; } -bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align) { +bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) { bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); dirty_lines = true; return res; diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index a34e745090..d0747a9e03 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -86,8 +86,8 @@ public: void clear_dropcap(); bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); - bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER); void set_align(HAlign p_align); HAlign get_align() const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 737b47ed95..fcd31143a8 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -62,11 +62,6 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { "top_right_corner" }; -// --- Plugins --- -Vector<TileSetPlugin *> TileSet::get_tile_set_atlas_plugins() const { - return tile_set_plugins_vector; -} - // -- Shape and layout -- void TileSet::set_tile_shape(TileSet::TileShape p_shape) { tile_shape = p_shape; @@ -205,21 +200,11 @@ void TileSet::set_uv_clipping(bool p_uv_clipping) { uv_clipping = p_uv_clipping; emit_changed(); } + bool TileSet::is_uv_clipping() const { return uv_clipping; }; -void TileSet::set_y_sorting(bool p_y_sort) { - if (y_sorting == p_y_sort) { - return; - } - y_sorting = p_y_sort; - emit_changed(); -} -bool TileSet::is_y_sorting() const { - return y_sorting; -}; - void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) { ERR_FAIL_COND(p_occlusion_layers_count < 0); if (occlusion_layers.size() == p_occlusion_layers_count) { @@ -2604,8 +2589,6 @@ void TileSet::_bind_methods() { // Rendering. ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); - ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting); - ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting); ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); @@ -2670,7 +2653,6 @@ void TileSet::_bind_methods() { ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); ADD_GROUP("Physics", ""); @@ -2727,12 +2709,6 @@ TileSet::TileSet() { // Instantiate the tile meshes. tile_lines_mesh.instantiate(); tile_filled_mesh.instantiate(); - - // Instantiate and list all plugins. - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); - tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); } TileSet::~TileSet() { @@ -2744,9 +2720,6 @@ TileSet::~TileSet() { while (!source_ids.is_empty()) { remove_source(source_ids[0]); } - for (int i = 0; i < tile_set_plugins_vector.size(); i++) { - memdelete(tile_set_plugins_vector[i]); - } } /////////////////////////////// TileSetSource ////////////////////////////////////// @@ -2972,23 +2945,22 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; E_alternative->get()->get_property_list(&alternative_property_list); - for (List<PropertyInfo>::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) { - property_info = E_property->get(); + for (PropertyInfo &alternative_property_info : alternative_property_list) { bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid); - Variant value = E_alternative->get()->get(property_info.name); + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); + Variant value = E_alternative->get()->get(alternative_property_info.name); if (valid && value == default_value) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } - property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name); - tile_property_list.push_back(property_info); + alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); + tile_property_list.push_back(alternative_property_info); } } // Add all alternative. - for (List<PropertyInfo>::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) { - E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name); - p_list->push_back(E_property->get()); + for (PropertyInfo &tile_property_info : tile_property_list) { + tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name); + p_list->push_back(tile_property_info); } } } @@ -3817,6 +3789,7 @@ int TileData::get_terrain_set() const { } void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { + ERR_FAIL_INDEX(p_peering_bit, TileSet::CellNeighbor::CELL_NEIGHBOR_MAX); ERR_FAIL_COND(terrain_set < 0); ERR_FAIL_COND(p_terrain_index < -1); if (tile_set) { @@ -4238,842 +4211,3 @@ void TileData::_bind_methods() { ADD_SIGNAL(MethodInfo("changed")); } -/////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// - -void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { - bool visible = p_tile_map->is_visible_in_tree(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - - // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - Transform2D xform; - xform.set_origin(E_cell->key()); - for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { - RS::get_singleton()->canvas_light_occluder_set_enabled(E_occluder_id->get(), visible); - } - } - } - } break; - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - if (!p_tile_map->is_inside_tree()) { - return; - } - - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = p_tile_map->get_quadrant_map().front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - - // Update occluders transform. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - Transform2D xform; - xform.set_origin(E_cell->key()); - for (List<RID>::Element *E_occluder_id = q.occluders.front(); E_occluder_id; E_occluder_id = E_occluder_id->next()) { - RS::get_singleton()->canvas_light_occluder_set_transform(E_occluder_id->get(), p_tile_map->get_global_transform() * xform); - } - } - } - } break; - case CanvasItem::NOTIFICATION_DRAW: { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - if (tile_set.is_valid() || p_tile_map->is_y_sort_enabled()) { - RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(p_tile_map->get_canvas_item(), tile_set->is_y_sorting() || p_tile_map->is_y_sort_enabled()); - } - } break; - } -} - -void TileSetPluginAtlasRendering::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); - - // Compute the offset - Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); - Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); - - // Compute the destination rectangle in the CanvasItem. - Rect2 dest_rect; - dest_rect.size = source_rect.size; - dest_rect.size.x += fp_adjust; - dest_rect.size.y += fp_adjust; - - bool transpose = tile_data->get_transpose(); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h()) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v()) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Get the tile modulation. - Color modulate = tile_data->get_modulate(); - modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); - - // Draw the tile. - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } -} - -void TileSetPluginAtlasRendering::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - bool visible = p_tile_map->is_visible_in_tree(); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - RenderingServer *rs = RenderingServer::get_singleton(); - - // Free the canvas items. - for (const RID &E : q.canvas_items) { - rs->free(E); - } - q.canvas_items.clear(); - - // Free the occluders. - for (const RID &E : q.occluders) { - rs->free(E); - } - q.occluders.clear(); - - // Those allow to group cell per material or z-index. - Ref<ShaderMaterial> prev_material; - int prev_z_index = 0; - RID prev_canvas_item; - - // Iterate over the cells of the quadrant. - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->value(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get the tile data. - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - Ref<ShaderMaterial> mat = tile_data->tile_get_material(); - int z_index = tile_data->get_z_index(); - - // Quandrant pos. - Vector2 position = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()); - if (tile_set->is_y_sorting()) { - // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. - position.y += tile_data->get_y_sort_origin(); - } - - // --- CanvasItems --- - // Create two canvas items, for rendering and debug. - RID canvas_item; - - // Check if the material or the z_index changed. - if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { - // If so, create a new CanvasItem. - canvas_item = rs->canvas_item_create(); - if (mat.is_valid()) { - rs->canvas_item_set_material(canvas_item, mat->get_rid()); - } - rs->canvas_item_set_parent(canvas_item, p_tile_map->get_canvas_item()); - rs->canvas_item_set_use_parent_material(canvas_item, p_tile_map->get_use_parent_material() || p_tile_map->get_material().is_valid()); - - Transform2D xform; - xform.set_origin(position); - rs->canvas_item_set_transform(canvas_item, xform); - - rs->canvas_item_set_light_mask(canvas_item, p_tile_map->get_light_mask()); - rs->canvas_item_set_z_index(canvas_item, z_index); - - rs->canvas_item_set_default_texture_filter(canvas_item, RS::CanvasItemTextureFilter(p_tile_map->CanvasItem::get_texture_filter())); - rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(p_tile_map->CanvasItem::get_texture_repeat())); - - q.canvas_items.push_back(canvas_item); - - prev_canvas_item = canvas_item; - prev_material = mat; - prev_z_index = z_index; - - } else { - // Keep the same canvas_item to draw on. - canvas_item = prev_canvas_item; - } - - // Drawing the tile in the canvas item. - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, p_tile_map->get_self_modulate()); - - // --- Occluders --- - for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { - Transform2D xform; - xform.set_origin(E_cell->key()); - if (tile_data->get_occluder(i).is_valid()) { - RID occluder_id = rs->canvas_light_occluder_create(); - rs->canvas_light_occluder_set_enabled(occluder_id, visible); - rs->canvas_light_occluder_set_transform(occluder_id, p_tile_map->get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder_id, p_tile_map->get_canvas()); - rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); - q.occluders.push_back(occluder_id); - } - } - } - } - } - - quadrant_order_dirty = true; - q_list_element = q_list_element->next(); - } - - // Reset the drawing indices - if (quadrant_order_dirty) { - int index = -(int64_t)0x80000000; //always must be drawn below children. - - // Sort the quadrants coords per world coordinates - Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - world_to_map[p_tile_map->map_to_world(E->key())] = E->key(); - } - - // Sort the quadrants - for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) { - TileMapQuadrant &q = quadrant_map[E->value()]; - for (const RID &F : q.canvas_items) { - RS::get_singleton()->canvas_item_set_draw_index(F, index++); - } - } - - quadrant_order_dirty = false; - } -} - -void TileSetPluginAtlasRendering::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - quadrant_order_dirty = true; -} - -void TileSetPluginAtlasRendering::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Free the canvas items. - for (const RID &E : p_quadrant->canvas_items) { - RenderingServer::get_singleton()->free(E); - } - p_quadrant->canvas_items.clear(); - - // Free the occluders. - for (const RID &E : p_quadrant->occluders) { - RenderingServer::get_singleton()->free(E); - } - p_quadrant->occluders.clear(); -} - -void TileSetPluginAtlasRendering::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for scenes needing one. - RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (!atlas_source->get_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.get_atlas_coords()); - to_hash.push_back(c.alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw a placeholder tile. - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } - } -} - -/////////////////////////////// TileSetPluginAtlasPhysics ////////////////////////////////////// - -void TileSetPluginAtlasPhysics::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - // Update the bodies transforms. - if (p_tile_map->is_inside_tree()) { - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - Transform2D global_transform = p_tile_map->get_global_transform(); - - for (Map<Vector2i, TileMapQuadrant>::Element *E = quadrant_map.front(); E; E = E->next()) { - TileMapQuadrant &q = E->get(); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E->key() * p_tile_map->get_effective_quadrant_size())); - xform = global_transform * xform; - - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - } break; - } -} - -void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - Transform2D global_transform = p_tile_map->get_global_transform(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - Vector2 quadrant_pos = p_tile_map->map_to_world(q.coords * p_tile_map->get_effective_quadrant_size()); - - // Clear shapes. - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - ps->body_clear_shapes(q.bodies[body_index]); - - // Position the bodies. - Transform2D xform; - xform.set_origin(quadrant_pos); - xform = global_transform * xform; - ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - - for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - for (int body_index = 0; body_index < q.bodies.size(); body_index++) { - // Add the shapes again. - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); - float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); - - int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); - for (int shape_index = 0; shape_index < shapes_count; shape_index++) { - Transform2D xform = Transform2D(); - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - - // Add decomposed convex shapes. - Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); - ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); - ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); - ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); - } - } - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginAtlasPhysics::create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - //Get the TileMap's gobla transform. - Transform2D global_transform; - if (p_tile_map->is_inside_tree()) { - global_transform = p_tile_map->get_global_transform(); - } - - // Clear all bodies. - p_quadrant->bodies.clear(); - - // Create the body and set its parameters. - for (int layer_index = 0; layer_index < tile_set->get_physics_layers_count(); layer_index++) { - RID body = PhysicsServer2D::get_singleton()->body_create(); - PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC); - - PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, p_tile_map->get_instance_id()); - PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer_index)); - PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer_index)); - - Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer_index); - if (!physics_material.is_valid()) { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); - } else { - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); - PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); - } - - if (p_tile_map->is_inside_tree()) { - RID space = p_tile_map->get_world_2d()->get_space(); - PhysicsServer2D::get_singleton()->body_set_space(body, space); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size())); - xform = global_transform * xform; - PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - - p_quadrant->bodies.push_back(body); - } -} - -void TileSetPluginAtlasPhysics::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Remove a quadrant. - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]); - } - p_quadrant->bodies.clear(); -} - -void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Draw the debug collision shapes. - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!p_tile_map->get_tree()) { - return; - } - - bool show_collision = false; - switch (p_tile_map->get_collision_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_collision = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_collision = true; - break; - } - if (!show_collision) { - return; - } - - RenderingServer *rs = RenderingServer::get_singleton(); - - Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); - - Color debug_collision_color = p_tile_map->get_tree()->get_debug_collisions_color(); - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - - if (tile_set->has_source(c.source_id)) { - TileSetSource *source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { - // Draw the debug polygon. - Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); - if (polygon.size() >= 3) { - Vector<Color> color; - color.push_back(debug_collision_color); - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); - } - } - } - } - } - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D()); - } -}; - -/////////////////////////////// TileSetPluginAtlasNavigation ////////////////////////////////////// - -void TileSetPluginAtlasNavigation::tilemap_notification(TileMap *p_tile_map, int p_what) { - switch (p_what) { - case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: { - if (p_tile_map->is_inside_tree()) { - Map<Vector2i, TileMapQuadrant> quadrant_map = p_tile_map->get_quadrant_map(); - Transform2D tilemap_xform = p_tile_map->get_global_transform(); - for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) { - TileMapQuadrant &q = E_quadrant->get(); - for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) { - for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) { - RID region = E_region->get()[layer_index]; - if (!region.is_valid()) { - continue; - } - Transform2D tile_transform; - tile_transform.set_origin(p_tile_map->map_to_world(E_region->key())); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - } - } - } - } - } break; - } -} - -void TileSetPluginAtlasNavigation::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!p_tile_map); - ERR_FAIL_COND(!p_tile_map->is_inside_tree()); - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - // Get colors for debug. - SceneTree *st = SceneTree::get_singleton(); - Color debug_navigation_color; - bool debug_navigation = st && st->is_debugging_navigation_hint(); - if (debug_navigation) { - debug_navigation_color = st->get_debug_navigation_color(); - } - - Transform2D tilemap_xform = p_tile_map->get_global_transform(); - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; - if (!region.is_valid()) { - continue; - } - NavigationServer2D::get_singleton()->region_set_map(region, RID()); - } - } - q.navigation_regions.clear(); - - // Get the navigation polygons and create regions. - for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count()); - - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref<NavigationPolygon> navpoly; - navpoly = tile_data->get_navigation_polygon(layer_index); - - if (navpoly.is_valid()) { - Transform2D tile_transform; - tile_transform.set_origin(p_tile_map->map_to_world(E_cell->get())); - - RID region = NavigationServer2D::get_singleton()->region_create(); - NavigationServer2D::get_singleton()->region_set_map(region, p_tile_map->get_world_2d()->get_navigation_map()); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); - q.navigation_regions[E_cell->get()].write[layer_index] = region; - } - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginAtlasNavigation::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Clear navigation shapes in the quadrant. - for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) { - for (int i = 0; i < E->get().size(); i++) { - RID region = E->get()[i]; - if (!region.is_valid()) { - continue; - } - NavigationServer2D::get_singleton()->free(region); - } - } - p_quadrant->navigation_regions.clear(); -} - -void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Draw the debug collision shapes. - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!p_tile_map->get_tree()) { - return; - } - - bool show_navigation = false; - switch (p_tile_map->get_navigation_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_navigation = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint()); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_navigation = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_navigation = true; - break; - } - if (!show_navigation) { - return; - } - - RenderingServer *rs = RenderingServer::get_singleton(); - - Color color = p_tile_map->get_tree()->get_debug_navigation_color(); - RandomPCG rand; - - Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); - - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); - - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); - if (navpoly.is_valid()) { - PackedVector2Array navigation_polygon_vertices = navpoly->get_vertices(); - - for (int i = 0; i < navpoly->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector<int> polygon = navpoly->get_polygon(i); - Vector<Vector2> vertices; - vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); - vertices.write[j] = navigation_polygon_vertices[polygon[j]]; - } - - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); - random_variation_color.a = color.a; - Vector<Color> colors; - colors.push_back(random_variation_color); - - rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, vertices, colors); - } - } - } - } - } - } -} - -/////////////////////////////// TileSetPluginScenesCollections ////////////////////////////////////// - -void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - - // Clear the scenes. - for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) { - Node *node = p_tile_map->get_node(E->get()); - if (node) { - node->queue_delete(); - } - } - - q.scenes.clear(); - - // Recreate the scenes. - for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); - if (packed_scene.is_valid()) { - Node *scene = packed_scene->instantiate(); - p_tile_map->add_child(scene); - Control *scene_as_control = Object::cast_to<Control>(scene); - Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); - if (scene_as_control) { - scene_as_control->set_position(p_tile_map->map_to_world(E_cell->get()) + scene_as_control->get_position()); - } else if (scene_as_node2d) { - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get())); - scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); - } - q.scenes[E_cell->get()] = scene->get_name(); - } - } - } - } - - q_list_element = q_list_element->next(); - } -} - -void TileSetPluginScenesCollections::cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - // Clear the scenes. - for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) { - Node *node = p_tile_map->get_node(E->get()); - if (node) { - node->queue_delete(); - } - } - - p_quadrant->scenes.clear(); -} - -void TileSetPluginScenesCollections::draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) { - Ref<TileSet> tile_set = p_tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for scenes needing one. - RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); - for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } - - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw a placeholder tile. - Transform2D xform; - xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); - rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } - } -} diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 0a07981171..35e6999d13 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -191,7 +191,6 @@ private: Vector2 tile_skew = Vector2(0, 0); // Rendering. - bool y_sorting = false; bool uv_clipping = false; struct OcclusionLayer { uint32_t light_mask = 1; @@ -245,9 +244,6 @@ private: int next_source_id = 0; // --------------------- - // Plugins themselves. - Vector<TileSetPlugin *> tile_set_plugins_vector; - void _compute_next_source_id(); void _source_changed(); @@ -299,9 +295,6 @@ public: Ref<TileSetSource> get_source(int p_source_id) const; // Rendering - void set_y_sorting(bool p_y_sort); - bool is_y_sorting() const; - void set_uv_clipping(bool p_uv_clipping); bool is_uv_clipping() const; @@ -666,73 +659,6 @@ public: Variant get_custom_data_by_layer_id(int p_layer_id) const; }; -#include "scene/2d/tile_map.h" - -class TileSetPlugin : public Object { - GDCLASS(TileSetPlugin, Object); - -public: - // Tilemap updates. - virtual void tilemap_notification(TileMap *p_tile_map, int p_what){}; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list){}; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; - - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant){}; -}; - -class TileSetPluginAtlasRendering : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasRendering, TileSetPlugin); - -private: - static constexpr float fp_adjust = 0.00001; - bool quadrant_order_dirty = false; - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - - // Other. - static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); -}; - -class TileSetPluginAtlasPhysics : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void create_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - -class TileSetPluginAtlasNavigation : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasNavigation, TileSetPlugin); - -public: - // Tilemap updates - virtual void tilemap_notification(TileMap *p_tile_map, int p_what) override; - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - -class TileSetPluginScenesCollections : public TileSetPlugin { - GDCLASS(TileSetPluginScenesCollections, TileSetPlugin); - -public: - // Tilemap updates - virtual void update_dirty_quadrants(TileMap *p_tile_map, SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) override; - virtual void cleanup_quadrant(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; - virtual void draw_quadrant_debug(TileMap *p_tile_map, TileMapQuadrant *p_quadrant) override; -}; - VARIANT_ENUM_CAST(TileSet::CellNeighbor); VARIANT_ENUM_CAST(TileSet::TerrainMode); VARIANT_ENUM_CAST(TileSet::TileShape); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 7292728251..a6815da6f4 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -451,18 +451,29 @@ VisualShader::Type VisualShader::get_shader_type() const { return current_type; } -void VisualShader::set_version(const String &p_version) { - version = p_version; +void VisualShader::set_engine_version(const Dictionary &p_engine_version) { + ERR_FAIL_COND(!p_engine_version.has("major")); + ERR_FAIL_COND(!p_engine_version.has("minor")); + engine_version["major"] = p_engine_version["major"]; + engine_version["minor"] = p_engine_version["minor"]; } -String VisualShader::get_version() const { - return version; +Dictionary VisualShader::get_engine_version() const { + return engine_version; } -void VisualShader::update_version(const String &p_new_version) { - if (version == "") { +#ifndef DISABLE_DEPRECATED + +void VisualShader::update_engine_version(const Dictionary &p_new_version) { + if (engine_version.is_empty()) { // before 4.0 for (int i = 0; i < TYPE_MAX; i++) { for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr()); + if (input.is_valid()) { + if (input->get_input_name() == "side") { + input->set_input_name("front_facing"); + } + } Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()); if (expression.is_valid()) { for (int j = 0; j < expression->get_input_port_count(); j++) { @@ -491,9 +502,11 @@ void VisualShader::update_version(const String &p_new_version) { } } } - set_version(p_new_version); + set_engine_version(p_new_version); } +#endif /* DISABLE_DEPRECATED */ + void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) { ERR_FAIL_COND(p_node.is_null()); ERR_FAIL_COND(p_id < 2); @@ -1635,19 +1648,10 @@ void VisualShader::_update_shader() const { { //fill render mode enums int idx = 0; - bool specular = false; while (render_mode_enums[idx].string) { if (shader_mode == render_mode_enums[idx].mode) { - if (shader_mode == Shader::MODE_SPATIAL) { - if (String(render_mode_enums[idx].string) == "specular") { - specular = true; - } - } - if (modes.has(render_mode_enums[idx].string) || specular) { - int which = 0; - if (modes.has(render_mode_enums[idx].string)) { - which = modes[render_mode_enums[idx].string]; - } + if (modes.has(render_mode_enums[idx].string)) { + int which = modes[render_mode_enums[idx].string]; int count = 0; for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)).size(); i++) { String mode = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode))[i]; @@ -2007,8 +2011,8 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); - ClassDB::bind_method(D_METHOD("set_version", "version"), &VisualShader::set_version); - ClassDB::bind_method(D_METHOD("get_version"), &VisualShader::get_version); + ClassDB::bind_method(D_METHOD("set_engine_version", "version"), &VisualShader::set_engine_version); + ClassDB::bind_method(D_METHOD("get_engine_version"), &VisualShader::get_engine_version); ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset); @@ -2016,7 +2020,7 @@ void VisualShader::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_version", "get_version"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "engine_version", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_engine_version", "get_engine_version"); ADD_PROPERTY_DEFAULT("code", ""); // Inherited from Shader, prevents showing default code as override in docs. @@ -2059,7 +2063,9 @@ VisualShader::VisualShader() { /////////////////////////////////////////////////////////// const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { - // Spatial, Vertex + // Node3D + + // Node3D, Vertex { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, @@ -2069,6 +2075,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview", "MODELVIEW_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -2079,7 +2089,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, - // Spatial, Fragment + // Node3D, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, @@ -2092,7 +2102,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec3(POINT_COORD, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "camera", "CAMERA_MATRIX" }, @@ -2103,11 +2112,14 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_roughness_texture", "NORMAL_ROUGHNESS_TEXTURE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" }, - // Spatial, Light + // Node3D, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" }, @@ -2127,6 +2139,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, + // Canvas Item + // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" }, @@ -2139,6 +2153,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, // Canvas Item, Fragment { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, @@ -2157,6 +2173,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" }, // Canvas Item, Light { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, @@ -2170,7 +2187,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_MODULATE.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow", "SHADOW_MODULATE.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "texture_pixel_size", "vec3(TEXTURE_PIXEL_SIZE, 1.0)" }, @@ -2810,7 +2827,11 @@ VisualShaderNodeUniformRef::VisualShaderNodeUniformRef() { //////////////////////////////////////////// const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { - // Spatial, Vertex + //////////////////////////////////////////////////////////////////////// + // Node3D. + //////////////////////////////////////////////////////////////////////// + // Node3D, Vertex. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, @@ -2821,9 +2842,9 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" }, - - // Spatial, Fragment - + //////////////////////////////////////////////////////////////////////// + // Node3D, Fragment. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, @@ -2847,29 +2868,48 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, - - // Spatial, Light + //////////////////////////////////////////////////////////////////////// + // Node3D, Light. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, - // Canvas Item, Vertex + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + + //////////////////////////////////////////////////////////////////////// + // Canvas Item. + //////////////////////////////////////////////////////////////////////// + // Canvas Item, Vertex. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + //////////////////////////////////////////////////////////////////////// + // Canvas Item, Fragment. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, - // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_vertex", "SHADOW_VERTEX:xy" }, + //////////////////////////////////////////////////////////////////////// + // Canvas Item, Light. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, - // Sky, Sky + //////////////////////////////////////////////////////////////////////// + // Sky, Sky. + //////////////////////////////////////////////////////////////////////// { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "fog", "FOG.rgb" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" }, + //////////////////////////////////////////////////////////////////////// { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, }; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 880c401b29..abf55185ab 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -44,7 +44,7 @@ class VisualShader : public Shader { friend class VisualShaderNodeVersionChecker; - String version = ""; + Dictionary engine_version; public: enum Type { @@ -137,10 +137,12 @@ public: // internal methods Type get_shader_type() const; public: - void set_version(const String &p_version); - String get_version() const; + void set_engine_version(const Dictionary &p_version); + Dictionary get_engine_version() const; - void update_version(const String &p_new_version); +#ifndef DISABLE_DEPRECATED + void update_engine_version(const Dictionary &p_new_version); +#endif /* DISABLE_DEPRECATED */ enum { NODE_ID_INVALID = -1, |