diff options
Diffstat (limited to 'scene')
154 files changed, 3738 insertions, 1733 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 4734f97e23..b33c2f43d4 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -388,6 +388,7 @@ void AnimatedSprite2D::play(const StringName &p_animation, const bool p_backward } } + is_over = false; set_playing(true); } diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 70b9b769cd..9699f784a2 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -137,14 +137,14 @@ void Area2D::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -152,13 +152,13 @@ void Area2D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -169,7 +169,7 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, BodyState>::Element *E = body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid); if (!body_in && !E) { return; //does not exist because it was likely removed from the tree @@ -180,36 +180,36 @@ void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i if (body_in) { if (!E) { E = body_map.insert(objid, BodyState()); - E->get().rid = p_body; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_body; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape)); } - if (!node || E->get().in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - body_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + body_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree)); @@ -231,14 +231,14 @@ void Area2D::_area_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->area_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -246,13 +246,13 @@ void Area2D::_area_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->area_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -263,7 +263,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, AreaState>::Element *E = area_map.find(objid); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(objid); if (!area_in && !E) { return; //likely removed from the tree @@ -273,36 +273,36 @@ void Area2D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i if (area_in) { if (!E) { E = area_map.insert(objid, AreaState()); - E->get().rid = p_area; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_area; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); } - if (!node || E->get().in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - area_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + area_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree)); @@ -323,7 +323,7 @@ void Area2D::_clear_monitoring() { ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal."); { - Map<ObjectID, BodyState> bmcopy = body_map; + HashMap<ObjectID, BodyState> bmcopy = body_map; body_map.clear(); //disconnect all monitored stuff @@ -351,7 +351,7 @@ void Area2D::_clear_monitoring() { } { - Map<ObjectID, AreaState> bmcopy = area_map; + HashMap<ObjectID, AreaState> bmcopy = area_map; area_map.clear(); //disconnect all monitored stuff @@ -461,20 +461,20 @@ TypedArray<Area2D> Area2D::get_overlapping_areas() const { bool Area2D::overlaps_area(Node *p_area) const { ERR_FAIL_NULL_V(p_area, false); - const Map<ObjectID, AreaState>::Element *E = area_map.find(p_area->get_instance_id()); + HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } bool Area2D::overlaps_body(Node *p_body) const { ERR_FAIL_NULL_V(p_body, false); - const Map<ObjectID, BodyState>::Element *E = body_map.find(p_body->get_instance_id()); + HashMap<ObjectID, BodyState>::ConstIterator E = body_map.find(p_body->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } void Area2D::set_audio_bus_override(bool p_override) { diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index a42e7722b0..a584420ced 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -94,7 +94,7 @@ private: VSet<ShapePair> shapes; }; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; void _area_inout(int p_status, const RID &p_area, ObjectID p_instance, int p_area_shape, int p_self_shape); @@ -126,7 +126,7 @@ private: VSet<AreaShapePair> shapes; }; - Map<ObjectID, AreaState> area_map; + HashMap<ObjectID, AreaState> area_map; void _clear_monitoring(); bool audio_bus_override = false; diff --git a/scene/2d/audio_listener_2d.cpp b/scene/2d/audio_listener_2d.cpp index eb463864e1..f7dd20d7c0 100644 --- a/scene/2d/audio_listener_2d.cpp +++ b/scene/2d/audio_listener_2d.cpp @@ -57,7 +57,7 @@ bool AudioListener2D::_get(const StringName &p_name, Variant &r_ret) const { } void AudioListener2D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "current")); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("current"))); } void AudioListener2D::_notification(int p_what) { diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index c1328badfb..624ef70468 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -148,7 +148,7 @@ void AudioStreamPlayer2D::_update_panning() { Vector2 global_pos = get_global_position(); - Set<Viewport *> viewports = world_2d->get_viewports(); + RBSet<Viewport *> viewports = world_2d->get_viewports(); viewports.insert(get_viewport()); // TODO: This is a mediocre workaround for #50958. Remove when that bug is fixed! volume_vector.resize(4); diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 50863f2c4d..a8c12f4893 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -606,7 +606,7 @@ void CollisionObject2D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_shape_entered", PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_shape_exited", PropertyInfo(Variant::INT, "shape_idx"))); - ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,Make Static,Keep Active"), "set_disable_mode", "get_disable_mode"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index f2b7eecc7b..997afee6c4 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -75,7 +75,7 @@ private: int total_subshapes = 0; - Map<uint32_t, ShapeData> shapes; + RBMap<uint32_t, ShapeData> shapes; bool only_update_transform_changes = false; // This is used for sync to physics. void _apply_disabled(); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 91549d75f0..59a7dc819f 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -102,6 +102,26 @@ void NavigationAgent2D::_notification(int p_what) { set_physics_process_internal(true); } break; + case NOTIFICATION_PAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_EXIT_TREE: { agent_parent = nullptr; set_physics_process_internal(false); diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 1447e25e8c..2a401190d0 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -41,6 +41,7 @@ class NavigationAgent2D : public Node { Node2D *agent_parent = nullptr; RID agent; + RID map_before_pause; uint32_t navigable_layers = 1; diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index d1e5bc11bc..0320c6c917 100644 --- a/scene/2d/navigation_obstacle_2d.cpp +++ b/scene/2d/navigation_obstacle_2d.cpp @@ -31,10 +31,13 @@ #include "navigation_obstacle_2d.h" #include "scene/2d/collision_shape_2d.h" +#include "scene/2d/physics_body_2d.h" #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" void NavigationObstacle2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid); + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius); ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius); @@ -78,8 +81,28 @@ void NavigationObstacle2D::_notification(int p_what) { parent_node2d = nullptr; } break; + case NOTIFICATION_PAUSED: { + if (parent_node2d && !parent_node2d->can_process()) { + map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (parent_node2d && !parent_node2d->can_process()) { + map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node2d && parent_node2d->can_process() && !(map_before_pause == RID())) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (parent_node2d) { + if (parent_node2d && parent_node2d->is_inside_tree()) { NavigationServer2D::get_singleton()->agent_set_position(agent, parent_node2d->get_global_position()); } } break; @@ -103,6 +126,11 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const { warnings.push_back(RTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object.")); } + if (Object::cast_to<StaticBody2D>(get_parent())) { + warnings.push_back(RTR("The NavigationObstacle2D is intended for constantly moving bodies like CharacterBody2D or RigidDynamicBody2D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." + "\nNot constantly moving or complete static objects should be captured with a refreshed NavigationPolygon so agents can not only avoid them but also move along those objects outline at high detail")); + } + return warnings; } @@ -122,13 +150,13 @@ void NavigationObstacle2D::reevaluate_agent_radius() { } real_t NavigationObstacle2D::estimate_agent_radius() const { - if (parent_node2d) { + if (parent_node2d && parent_node2d->is_inside_tree()) { // Estimate the radius of this physics body real_t radius = 0.0; for (int i(0); i < parent_node2d->get_child_count(); i++) { // For each collision shape CollisionShape2D *cs = Object::cast_to<CollisionShape2D>(parent_node2d->get_child(i)); - if (cs) { + if (cs && cs->is_inside_tree()) { // Take the distance between the Body center to the shape center real_t r = cs->get_transform().get_origin().length(); if (cs->get_shape().is_valid()) { @@ -139,6 +167,9 @@ real_t NavigationObstacle2D::estimate_agent_radius() const { r *= MAX(s.x, s.y); // Takes the biggest radius radius = MAX(radius, r); + } else if (cs && !cs->is_inside_tree()) { + WARN_PRINT("A CollisionShape2D of the NavigationObstacle2D parent node was not inside the SceneTree when estimating the obstacle radius." + "\nMove the NavigationObstacle2D to a child position below any CollisionShape2D node of the parent node so the CollisionShape2D is already inside the SceneTree."); } } Vector2 s = parent_node2d->get_global_scale(); diff --git a/scene/2d/navigation_obstacle_2d.h b/scene/2d/navigation_obstacle_2d.h index 2a0ef14e73..948cf5b61a 100644 --- a/scene/2d/navigation_obstacle_2d.h +++ b/scene/2d/navigation_obstacle_2d.h @@ -39,6 +39,7 @@ class NavigationObstacle2D : public Node { Node2D *parent_node2d = nullptr; RID agent; + RID map_before_pause; bool estimate_radius = true; real_t radius = 1.0; diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 3cc9f3f2c4..260faf1d68 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -302,19 +302,19 @@ void NavigationPolygon::make_polygons_from_outlines() { polygons.clear(); vertices.clear(); - Map<Vector2, int> points; + HashMap<Vector2, int> points; for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { TPPLPoly &tp = I->get(); struct Polygon p; for (int64_t i = 0; i < tp.GetNumPoints(); i++) { - Map<Vector2, int>::Element *E = points.find(tp[i]); + HashMap<Vector2, int>::Iterator E = points.find(tp[i]); if (!E) { E = points.insert(tp[i], vertices.size()); vertices.push_back(tp[i]); } - p.indices.push_back(E->get()); + p.indices.push_back(E->value); } polygons.push_back(p); @@ -509,6 +509,9 @@ void NavigationRegion2D::_navpoly_changed() { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_navigation_hint())) { update(); } + if (navpoly.is_valid()) { + NavigationServer2D::get_singleton()->region_set_navpoly(region, navpoly); + } } void NavigationRegion2D::_map_changed(RID p_map) { if (enabled && get_world_2d()->get_navigation_map() == p_map) { diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 88f68e4142..9d9db0d6b4 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -56,13 +56,11 @@ PhysicsBody2D::~PhysicsBody2D() { Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p_test_only, real_t p_margin) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. PhysicsServer2D::MotionResult result; - bool collided = move_and_collide(parameters, result, p_test_only); - - // Don't report collision when the whole motion is done. - if (collided && result.collision_safe_fraction < 1) { + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -143,15 +141,9 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_distan } PhysicsServer2D::MotionParameters parameters(p_from, p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. - bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); - - if (colliding) { - // Don't report collision when the whole motion is done. - return (r->collision_safe_fraction < 1.0); - } else { - return false; - } + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r); } TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { @@ -339,17 +331,17 @@ void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) { ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_scene); + ERR_FAIL_COND(E->value.in_scene); contact_monitor->locked = true; - E->get().in_scene = true; + E->value.in_scene = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -360,17 +352,17 @@ void RigidDynamicBody2D::_body_exit_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_scene); - E->get().in_scene = false; + ERR_FAIL_COND(!E->value.in_scene); + E->value.in_scene = false; contact_monitor->locked = true; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -384,45 +376,45 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid); ERR_FAIL_COND(!body_in && !E); if (body_in) { if (!E) { E = contact_monitor->body_map.insert(objid, BodyState()); - E->get().rid = p_body; - //E->get().rc=0; - E->get().in_scene = node && node->is_inside_tree(); + E->value.rid = p_body; + //E->value.rc=0; + E->value.in_scene = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree), make_binds(objid)); - if (E->get().in_scene) { + if (E->value.in_scene) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } - //E->get().rc++; + //E->value.rc++; } if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape)); } - if (E->get().in_scene) { + if (E->value.in_scene) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape); } } else { - //E->get().rc--; + //E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape)); } - bool in_scene = E->get().in_scene; + bool in_scene = E->value.in_scene; - if (E->get().shapes.is_empty()) { + if (E->value.shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody2D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody2D::_body_exit_tree)); @@ -431,7 +423,7 @@ void RigidDynamicBody2D::_body_inout(int p_status, const RID &p_body, ObjectID p } } - contact_monitor->body_map.erase(E); + contact_monitor->body_map.remove(E); } if (node && in_scene) { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, node, p_body_shape, p_local_shape); @@ -494,7 +486,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) int local_shape = p_state->get_contact_local_shape(i); int shape = p_state->get_contact_collider_shape(i); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj); if (!E) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -505,7 +497,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } ShapePair sp(shape, local_shape); - int idx = E->get().shapes.find(sp); + int idx = E->value.shapes.find(sp); if (idx == -1) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -515,7 +507,7 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) continue; } - E->get().shapes[idx].tagged = true; + E->value.shapes[idx].tagged = true; } //put the ones to remove @@ -1145,6 +1137,7 @@ bool CharacterBody2D::move_and_slide() { if (!current_platform_velocity.is_equal_approx(Vector2())) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1203,6 +1196,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. Vector2 prev_position = parameters.from.columns[2]; @@ -1359,6 +1353,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer2D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1405,6 +1400,7 @@ void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; @@ -1440,6 +1436,7 @@ bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f real_t length = MAX(floor_snap_length, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer2D::MotionResult result; diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 1e4483b4d0..7401fc7578 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -200,7 +200,7 @@ private: struct ContactMonitor { bool locked = false; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; }; ContactMonitor *contact_monitor = nullptr; diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index 2c6f2d5c00..b809bc4b8e 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -44,7 +44,7 @@ class RayCast2D : public Node2D { int against_shape = 0; Vector2 collision_point; Vector2 collision_normal; - Set<RID> exclude; + RBSet<RID> exclude; uint32_t collision_mask = 1; bool exclude_parent_body = true; diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h index 15436d6e3d..78125b08bd 100644 --- a/scene/2d/shape_cast_2d.h +++ b/scene/2d/shape_cast_2d.h @@ -46,7 +46,7 @@ class ShapeCast2D : public Node2D { RID shape_rid; Vector2 target_position = Vector2(0, 50); - Set<RID> exclude; + RBSet<RID> exclude; real_t margin = 0.0; uint32_t collision_mask = 1; bool exclude_parent_body = true; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index aa039e07ee..377d51cad7 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -81,14 +81,14 @@ bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const { } void Bone2D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "auto_calculate_length_and_angle", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("auto_calculate_length_and_angle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); if (!autocalculate_length_and_angle) { - p_list->push_back(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "1, 1024, 1", PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::FLOAT, "bone_angle", PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("length"), PROPERTY_HINT_RANGE, "1, 1024, 1", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("bone_angle"), PROPERTY_HINT_RANGE, "-360, 360, 0.01", PROPERTY_USAGE_DEFAULT)); } #ifdef TOOLS_ENABLED - p_list->push_back(PropertyInfo(Variant::BOOL, "editor_settings/show_bone_gizmo", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("editor_settings/show_bone_gizmo"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT)); #endif // TOOLS_ENABLED } @@ -560,7 +560,7 @@ bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back( - PropertyInfo(Variant::OBJECT, "modification_stack", + PropertyInfo(Variant::OBJECT, PNAME("modification_stack"), PROPERTY_HINT_RESOURCE_TYPE, "SkeletonModificationStack2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index cab57146b1..c4b923ff34 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,8 +34,8 @@ #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" -Map<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const { - Map<Vector2i, TileSet::CellNeighbor> output; +HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const { + HashMap<Vector2i, TileSet::CellNeighbor> output; Ref<TileSet> tile_set = tile_map->get_tileset(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); @@ -742,7 +742,7 @@ Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coor 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) { +HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) { ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); TileMapQuadrant q; @@ -765,9 +765,9 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer, return layers[p_layer].quadrant_map.insert(p_qk, q); } -void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) { +void TileMap::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { // Make the given quadrant dirty, then trigger an update later. - TileMapQuadrant &q = Q->get(); + TileMapQuadrant &q = Q->value; if (!q.dirty_list_element.in_list()) { layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element); } @@ -810,8 +810,8 @@ void TileMap::_update_dirty_quadrants() { 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(); + for (const Vector2i &E : q->self()->cells) { + Vector2i pk = E; 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; @@ -871,18 +871,18 @@ void TileMap::_recreate_layer_internals(int p_layer) { _rendering_update_layer(p_layer); // Recreate the quadrants. - const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y)); - Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk); + HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk); if (!Q) { Q = _create_quadrant(p_layer, qk); - layers[p_layer].dirty_quadrant_list.add(&Q->get().dirty_list_element); + layers[p_layer].dirty_quadrant_list.add(&Q->value.dirty_list_element); } Vector2i pk = E.key; - Q->get().cells.insert(pk); + Q->value.cells.insert(pk); _make_quadrant_dirty(Q); } @@ -896,9 +896,9 @@ void TileMap::_recreate_internals() { } } -void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { +void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { // Remove a quadrant. - TileMapQuadrant *q = &(Q->get()); + TileMapQuadrant *q = &(Q->value); // Call the cleanup_quadrant method on plugins. if (tile_set.is_valid()) { @@ -917,7 +917,7 @@ void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) { RenderingServer *rs = RenderingServer::get_singleton(); rs->free(q->debug_canvas_item); - layers[q->layer].quadrant_map.erase(Q); + layers[q->layer].quadrant_map.remove(Q); rect_cache_dirty = true; } @@ -926,7 +926,7 @@ void TileMap::_clear_layer_internals(int p_layer) { // Clear quadrants. while (layers[p_layer].quadrant_map.size()) { - _erase_quadrant(layers[p_layer].quadrant_map.front()); + _erase_quadrant(layers[p_layer].quadrant_map.begin()); } // Clear the layers internals. @@ -954,15 +954,17 @@ void TileMap::_recompute_rect_cache() { } Rect2 r_total; + bool first = true; for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (const Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) { + for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { 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.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 (first) { r_total = r; + first = false; } else { r_total = r_total.merge(r); } @@ -1201,7 +1203,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List for (int layer = 0; layer < (int)layers.size(); layer++) { // Sort the quadrants coords per world coordinates - Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { world_to_map[map_to_world(E.key)] = E.key; } @@ -1248,8 +1250,8 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // 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); + for (const Vector2i &E_cell : p_quadrant->cells) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1279,7 +1281,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D xform; - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + xform.set_origin(map_to_world(E_cell) - 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); } @@ -1462,8 +1464,8 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r q.bodies.clear(); // Recreate bodies and shapes. - 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); + for (const Vector2i &E_cell : q.cells) { + TileMapCell c = get_cell(q.layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1476,8 +1478,8 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { const TileData *tile_data; - if (q.runtime_tile_data_cache.has(E_cell->get())) { - tile_data = q.runtime_tile_data_cache[E_cell->get()]; + if (q.runtime_tile_data_cache.has(E_cell)) { + tile_data = q.runtime_tile_data_cache[E_cell]; } else { tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); } @@ -1488,12 +1490,12 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r // Create the body. RID body = ps->body_create(); - bodies_coords[body] = E_cell->get(); + bodies_coords[body] = E_cell; ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); ps->body_set_space(body, space); Transform2D xform; - xform.set_origin(map_to_world(E_cell->get())); + xform.set_origin(map_to_world(E_cell)); xform = global_transform * xform; ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); @@ -1659,8 +1661,8 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List 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); + for (const Vector2i &E_cell : q.cells) { + TileMapCell c = get_cell(q.layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1673,12 +1675,12 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { const TileData *tile_data; - if (q.runtime_tile_data_cache.has(E_cell->get())) { - tile_data = q.runtime_tile_data_cache[E_cell->get()]; + if (q.runtime_tile_data_cache.has(E_cell)) { + tile_data = q.runtime_tile_data_cache[E_cell]; } else { tile_data = 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()); + q.navigation_regions[E_cell].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; @@ -1686,13 +1688,13 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (navpoly.is_valid()) { Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_cell->get())); + tile_transform.set_origin(map_to_world(E_cell)); 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.navigation_regions[E_cell].write[layer_index] = region; } } } @@ -1748,8 +1750,8 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { 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); + for (const Vector2i &E_cell : p_quadrant->cells) { + TileMapCell c = get_cell(p_quadrant->layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1762,14 +1764,14 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { const TileData *tile_data; - if (p_quadrant->runtime_tile_data_cache.has(E_cell->get())) { - tile_data = p_quadrant->runtime_tile_data_cache[E_cell->get()]; + if (p_quadrant->runtime_tile_data_cache.has(E_cell)) { + tile_data = p_quadrant->runtime_tile_data_cache[E_cell]; } else { tile_data = 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); + xform.set_origin(map_to_world(E_cell) - 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++) { @@ -1823,8 +1825,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ 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); + for (const Vector2i &E_cell : q.cells) { + const TileMapCell &c = get_cell(q.layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1843,13 +1845,13 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ 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()); + scene_as_control->set_position(map_to_world(E_cell) + scene_as_control->get_position()); } else if (scene_as_node2d) { Transform2D xform; - xform.set_origin(map_to_world(E_cell->get())); + xform.set_origin(map_to_world(E_cell)); scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); } - q.scenes[E_cell->get()] = scene->get_name(); + q.scenes[E_cell] = scene->get_name(); } } } @@ -1881,8 +1883,8 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // 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); + for (const Vector2i &E_cell : p_quadrant->cells) { + const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1910,7 +1912,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D xform; - xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos); + xform.set_origin(map_to_world(E_cell) - 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); } @@ -1923,9 +1925,9 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c 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; + HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector2i pk(p_coords); - Map<Vector2i, TileMapCell>::Element *E = tile_map.find(pk); + HashMap<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk); int source_id = p_source_id; Vector2i atlas_coords = p_atlas_coords; @@ -1946,7 +1948,7 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c // Get the quadrant Vector2i qk = _coords_to_quadrant_coords(p_layer, pk); - Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk); + HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk); if (source_id == TileSet::INVALID_SOURCE) { // Erase existing cell in the tile map. @@ -1954,7 +1956,7 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c // Erase existing cell in the quadrant. ERR_FAIL_COND(!Q); - TileMapQuadrant &q = Q->get(); + TileMapQuadrant &q = Q->value; q.cells.erase(pk); @@ -1975,18 +1977,18 @@ void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, c if (!Q) { Q = _create_quadrant(p_layer, qk); } - TileMapQuadrant &q = Q->get(); + TileMapQuadrant &q = Q->value; q.cells.insert(pk); } else { ERR_FAIL_COND(!Q); // TileMapQuadrant should exist... - if (E->get().source_id == source_id && E->get().get_atlas_coords() == atlas_coords && E->get().alternative_tile == alternative_tile) { + if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) { return; // Nothing changed. } } - TileMapCell &c = E->get(); + TileMapCell &c = E->value; c.source_id = source_id; c.set_atlas_coords(atlas_coords); @@ -2005,57 +2007,57 @@ int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_us 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); + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); if (!E) { return TileSet::INVALID_SOURCE; } if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); return proxyed[0]; } - return E->get().source_id; + return E->value.source_id; } 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); + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); if (!E) { return TileSetSource::INVALID_ATLAS_COORDS; } if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); return proxyed[1]; } - return E->get().get_atlas_coords(); + return E->value.get_atlas_coords(); } 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); + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); if (!E) { return TileSetSource::INVALID_TILE_ALTERNATIVE; } if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); return proxyed[2]; } - return E->get().alternative_tile; + return E->value.alternative_tile; } Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { @@ -2147,13 +2149,13 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat } } -Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints) { +RBSet<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) { if (!tile_set.is_valid()) { - return Set<TileSet::TerrainsPattern>(); + return RBSet<TileSet::TerrainsPattern>(); } // Returns all tiles compatible with the given constraints. - Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns; + RBSet<TileSet::TerrainsPattern> compatible_terrain_tile_patterns; for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) { int valid = true; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { @@ -2161,7 +2163,7 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) { // Check if the bit is compatible with the constraints. TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain(bit)); - Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint); + RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint); if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { valid = false; break; @@ -2177,34 +2179,34 @@ Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constrai return compatible_terrain_tile_patterns; } -Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const { +RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const { if (!tile_set.is_valid()) { - return Set<TerrainConstraint>(); + return RBSet<TerrainConstraint>(); } - ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<TerrainConstraint>()); - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Set<TerrainConstraint>()); + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>()); + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>()); // Build a set of dummy constraints get the constrained points. - Set<TerrainConstraint> dummy_constraints; - for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) { + RBSet<TerrainConstraint> dummy_constraints; + for (const Vector2i &E : p_to_replace) { for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides. TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) { - dummy_constraints.insert(TerrainConstraint(this, E->get(), bit, -1)); + dummy_constraints.insert(TerrainConstraint(this, E, bit, -1)); } } } // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it. - Set<TerrainConstraint> constraints; - for (Set<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) { - TerrainConstraint c = E->get(); + RBSet<TerrainConstraint> constraints; + for (const TerrainConstraint &E : dummy_constraints) { + TerrainConstraint c = E; - Map<int, int> terrain_count; + HashMap<int, int> terrain_count; // Count the number of occurrences per terrain. - Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); + HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) { if (!p_to_replace.has(E_overlapping.key)) { TileData *neighbor_tile_data = nullptr; @@ -2250,13 +2252,13 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce return constraints; } -Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { +RBSet<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { if (!tile_set.is_valid()) { - return Set<TerrainConstraint>(); + return RBSet<TerrainConstraint>(); } // Compute the constraints needed from the surrounding tiles. - Set<TerrainConstraint> output; + RBSet<TerrainConstraint> output; for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor side = TileSet::CellNeighbor(i); if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) { @@ -2268,35 +2270,35 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile return output; } -Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints) { +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) { if (!tile_set.is_valid()) { - return Map<Vector2i, TileSet::TerrainsPattern>(); + return HashMap<Vector2i, TileSet::TerrainsPattern>(); } // Copy the constraints set. - Set<TerrainConstraint> constraints = p_constraints; + RBSet<TerrainConstraint> constraints = p_constraints; // Compute all acceptable patterns for each cell. - Map<Vector2i, Set<TileSet::TerrainsPattern>> per_cell_acceptable_tiles; + HashMap<Vector2i, RBSet<TileSet::TerrainsPattern>> per_cell_acceptable_tiles; for (Vector2i cell : p_to_replace) { per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints); } // Output map. - Map<Vector2i, TileSet::TerrainsPattern> output; + HashMap<Vector2i, TileSet::TerrainsPattern> output; // Add all positions to a set. - Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace); + RBSet<Vector2i> to_replace = RBSet<Vector2i>(p_to_replace); while (!to_replace.is_empty()) { // Compute the minimum number of tile possibilities for each cell. int min_nb_possibilities = 100000000; - for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { + for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { min_nb_possibilities = MIN(min_nb_possibilities, E.value.size()); } // Get the set of possible cells to fill, out of the most constrained ones. LocalVector<Vector2i> to_choose_from; - for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { + for (const KeyValue<Vector2i, RBSet<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) { if (E.value.size() == min_nb_possibilities) { to_choose_from.push_back(E.key); } @@ -2306,7 +2308,7 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse( Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)]; // Get the list of acceptable patterns for the given cell. - Set<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace]; + RBSet<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace]; if (valid_tiles.is_empty()) { break; // No possibilities :/ } @@ -2317,7 +2319,7 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse( LocalVector<int> terrains_counts; int pattern_index = 0; for (const TileSet::TerrainsPattern &pattern : valid_tiles) { - Set<int> terrains; + RBSet<int> terrains; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor side = TileSet::CellNeighbor(i); if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) { @@ -2345,9 +2347,9 @@ Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse( per_cell_acceptable_tiles.erase(selected_cell_to_replace); // Add the new constraints from the added tiles. - Set<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern); - for (Set<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) { - constraints.insert(E_constraint->get()); + RBSet<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern); + for (const TerrainConstraint &E_constraint : new_constraints) { + constraints.insert(E_constraint); } // Compute valid tiles again for neighbors. @@ -2369,14 +2371,14 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); - Set<Vector2i> coords_set; + RBSet<Vector2i> coords_set; for (int i = 0; i < p_coords_array.size(); i++) { coords_set.insert(p_coords_array[i]); } - Set<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains); + RBSet<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains); - Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints); + HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints); for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) { TileMapCell cell = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile); @@ -2385,11 +2387,11 @@ void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector 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; + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; if (!tile_map.has(p_coords)) { return TileMapCell(); } else { - TileMapCell c = tile_map.find(p_coords)->get(); + TileMapCell c = tile_map.find(p_coords)->value; if (p_use_proxies && tile_set.is_valid()) { Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); c.source_id = proxyed[0]; @@ -2400,7 +2402,7 @@ TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_ } } -Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { +HashMap<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; @@ -2415,16 +2417,16 @@ void TileMap::fix_invalid_tiles() { ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); for (unsigned int i = 0; i < layers.size(); i++) { - const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; - Set<Vector2i> coords; + const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + RBSet<Vector2i> coords; for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { TileSetSource *source = *tile_set->get_source(E.value.source_id); if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) { coords.insert(E.key); } } - 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); + for (const Vector2i &E : coords) { + set_cell(i, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } } @@ -2546,7 +2548,7 @@ 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; + const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; Vector<int> data; data.resize(tile_map.size() * 3); int *w = data.ptrw(); @@ -3375,10 +3377,10 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache used_rect_cache = Rect2i(); for (unsigned int i = 0; i < layers.size(); i++) { - const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; + const HashMap<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); + used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0); first = false; } @@ -3448,8 +3450,8 @@ 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 (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 (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { + TileMapQuadrant &q = F->value; 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); @@ -3463,8 +3465,8 @@ 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 (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 (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { + TileMapQuadrant &q = F->value; 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); @@ -3512,7 +3514,7 @@ TypedArray<Vector2i> TileMap::get_surrounding_tiles(Vector2i coords) { return around; } -void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform) { +void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Color p_color, Transform2D p_transform) { if (!tile_set.is_valid()) { return; } @@ -3522,11 +3524,11 @@ void TileMap::draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Colo Vector<Vector2> polygon = tile_set->get_tile_shape_polygon(); TileSet::TileShape shape = tile_set->get_tile_shape(); - for (Set<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) { - Vector2 center = map_to_world(E->get()); + for (const Vector2i &E : p_cells) { + Vector2 center = map_to_world(E); #define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \ - if (!p_cells.has(get_neighbor_cell(E->get(), side))) { \ + if (!p_cells.has(get_neighbor_cell(E, side))) { \ Vector2 from = p_transform.xform(center + polygon[polygon_index_from] * tile_size); \ Vector2 to = p_transform.xform(center + polygon[polygon_index_to] * tile_size); \ p_control->draw_line(from, to, p_color); \ @@ -3576,7 +3578,7 @@ 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; + RBSet<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); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index a0655dea2a..02a2b3a1c6 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -57,11 +57,11 @@ struct TileMapQuadrant { Vector2i coords; // TileMapCells - Set<Vector2i> cells; + RBSet<Vector2i> cells; // We need those two maps to sort by world position for rendering // This is kind of workaround, it would be better to sort the cells directly in the "cells" set instead. - Map<Vector2i, Vector2i> map_to_world; - Map<Vector2i, Vector2i, CoordsWorldComparator> world_to_map; + RBMap<Vector2i, Vector2i> map_to_world; + RBMap<Vector2i, Vector2i, CoordsWorldComparator> world_to_map; // Debug. RID debug_canvas_item; @@ -74,13 +74,13 @@ struct TileMapQuadrant { List<RID> bodies; // Navigation. - Map<Vector2i, Vector<RID>> navigation_regions; + HashMap<Vector2i, Vector<RID>> navigation_regions; // Scenes. - Map<Vector2i, String> scenes; + HashMap<Vector2i, String> scenes; // Runtime TileData cache. - Map<Vector2i, TileData *> runtime_tile_data_cache; + HashMap<Vector2i, TileData *> runtime_tile_data_cache; void operator=(const TileMapQuadrant &q) { layer = q.layer; @@ -135,7 +135,7 @@ public: return base_cell_coords; } - Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const; + HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const; void set_terrain(int p_terrain) { terrain = p_terrain; @@ -193,22 +193,22 @@ private: int y_sort_origin = 0; int z_index = 0; RID canvas_item; - Map<Vector2i, TileMapCell> tile_map; - Map<Vector2i, TileMapQuadrant> quadrant_map; + HashMap<Vector2i, TileMapCell> tile_map; + HashMap<Vector2i, TileMapQuadrant> quadrant_map; SelfList<TileMapQuadrant>::List dirty_quadrant_list; }; LocalVector<TileMapLayer> layers; int selected_layer = -1; // Mapping for RID to coords. - Map<RID, Vector2i> bodies_coords; + HashMap<RID, Vector2i> bodies_coords; // Quadrants and internals management. Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const; - Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(int p_layer, const Vector2i &p_qk); + HashMap<Vector2i, TileMapQuadrant>::Iterator _create_quadrant(int p_layer, const Vector2i &p_qk); - void _make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q); + void _make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q); void _make_all_quadrants_dirty(); void _queue_update_dirty_quadrants(); @@ -217,7 +217,7 @@ private: void _recreate_layer_internals(int p_layer); void _recreate_internals(); - void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q); + void _erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q); void _clear_layer_internals(int p_layer); void _clear_internals(); @@ -251,7 +251,7 @@ private: void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant); // Terrains. - Set<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints); + RBSet<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints); // Set and get tiles from data arrays. void _set_tile_data(int p_layer, const Vector<int> &p_data); @@ -333,14 +333,14 @@ public: void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern); // Terrains. - Set<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed. - Set<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed. - Map<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints); // Not exposed. + RBSet<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const RBSet<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed. + RBSet<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const RBSet<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed. void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true); // Not exposed to users 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); + HashMap<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer); int get_effective_quadrant_size(int p_layer) const; //--- @@ -377,7 +377,7 @@ public: // 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()); + void draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D()); // Virtual function to modify the TileData at runtime GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i); diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp index 4bceaf71c6..2aae383cdf 100644 --- a/scene/2d/visible_on_screen_notifier_2d.cpp +++ b/scene/2d/visible_on_screen_notifier_2d.cpp @@ -198,7 +198,7 @@ void VisibleOnScreenEnabler2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler2D::get_enable_node_path); ADD_GROUP("Enabling", "enable_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,When Paused"), "set_enable_mode", "get_enable_mode"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path"); BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT); diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h index e0d580f174..3165eb92df 100644 --- a/scene/2d/visible_on_screen_notifier_2d.h +++ b/scene/2d/visible_on_screen_notifier_2d.h @@ -37,7 +37,7 @@ class Viewport; class VisibleOnScreenNotifier2D : public Node2D { GDCLASS(VisibleOnScreenNotifier2D, Node2D); - Set<Viewport *> viewports; + RBSet<Viewport *> viewports; Rect2 rect; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index fb37e64a1a..54737e13b3 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -192,14 +192,14 @@ void Area3D::_body_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -207,13 +207,13 @@ void Area3D::_body_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, BodyState>::Element *E = body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].area_shape); } } @@ -224,7 +224,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, BodyState>::Element *E = body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid); if (!body_in && !E) { return; //likely removed from the tree @@ -235,36 +235,36 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i if (body_in) { if (!E) { E = body_map.insert(objid, BodyState()); - E->get().rid = p_body; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_body; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape)); } - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - body_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + body_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree)); @@ -285,7 +285,7 @@ void Area3D::_clear_monitoring() { ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal."); { - Map<ObjectID, BodyState> bmcopy = body_map; + HashMap<ObjectID, BodyState> bmcopy = body_map; body_map.clear(); //disconnect all monitored stuff @@ -314,7 +314,7 @@ void Area3D::_clear_monitoring() { } { - Map<ObjectID, AreaState> bmcopy = area_map; + HashMap<ObjectID, AreaState> bmcopy = area_map; area_map.clear(); //disconnect all monitored stuff @@ -379,14 +379,14 @@ void Area3D::_area_enter_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; emit_signal(SceneStringNames::get_singleton()->area_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -394,13 +394,13 @@ void Area3D::_area_exit_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); - Map<ObjectID, AreaState>::Element *E = area_map.find(p_id); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; emit_signal(SceneStringNames::get_singleton()->area_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->value.rid, node, E->value.shapes[i].area_shape, E->value.shapes[i].self_shape); } } @@ -411,7 +411,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i Object *obj = ObjectDB::get_instance(objid); Node *node = Object::cast_to<Node>(obj); - Map<ObjectID, AreaState>::Element *E = area_map.find(objid); + HashMap<ObjectID, AreaState>::Iterator E = area_map.find(objid); if (!area_in && !E) { return; //likely removed from the tree @@ -422,36 +422,36 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i if (area_in) { if (!E) { E = area_map.insert(objid, AreaState()); - E->get().rid = p_area; - E->get().rc = 0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_area; + E->value.rc = 0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_entered, node); } } } - E->get().rc++; + E->value.rc++; if (node) { - E->get().shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.insert(AreaShapePair(p_area_shape, p_self_shape)); } - if (!node || E->get().in_tree) { + if (!node || E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape); } } else { - E->get().rc--; + E->value.rc--; if (node) { - E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); + E->value.shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); } - bool in_tree = E->get().in_tree; - if (E->get().rc == 0) { - area_map.erase(E); + bool in_tree = E->value.in_tree; + if (E->value.rc == 0) { + area_map.remove(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree)); @@ -524,20 +524,20 @@ TypedArray<Area3D> Area3D::get_overlapping_areas() const { bool Area3D::overlaps_area(Node *p_area) const { ERR_FAIL_NULL_V(p_area, false); - const Map<ObjectID, AreaState>::Element *E = area_map.find(p_area->get_instance_id()); + HashMap<ObjectID, AreaState>::ConstIterator E = area_map.find(p_area->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } bool Area3D::overlaps_body(Node *p_body) const { ERR_FAIL_NULL_V(p_body, false); - const Map<ObjectID, BodyState>::Element *E = body_map.find(p_body->get_instance_id()); + HashMap<ObjectID, BodyState>::ConstIterator E = body_map.find(p_body->get_instance_id()); if (!E) { return false; } - return E->get().in_tree; + return E->value.in_tree; } void Area3D::set_audio_bus_override(bool p_override) { diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 31ded00fb7..3b892baf57 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -98,7 +98,7 @@ private: VSet<ShapePair> shapes; }; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; void _area_inout(int p_status, const RID &p_area, ObjectID p_instance, int p_area_shape, int p_self_shape); @@ -130,7 +130,7 @@ private: VSet<AreaShapePair> shapes; }; - Map<ObjectID, AreaState> area_map; + HashMap<ObjectID, AreaState> area_map; void _clear_monitoring(); bool audio_bus_override = false; diff --git a/scene/3d/audio_listener_3d.cpp b/scene/3d/audio_listener_3d.cpp index 1ead9bb384..4f3f403ab7 100644 --- a/scene/3d/audio_listener_3d.cpp +++ b/scene/3d/audio_listener_3d.cpp @@ -68,7 +68,7 @@ bool AudioListener3D::_get(const StringName &p_name, Variant &r_ret) const { } void AudioListener3D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "current")); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("current"))); } void AudioListener3D::_update_listener() { diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index f057c72012..4a20a88346 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -281,7 +281,7 @@ void AudioStreamPlayer3D::_notification(int p_what) { active.set(); Ref<AudioStreamPlayback> new_playback = stream->instance_playback(); ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback."); - Map<StringName, Vector<AudioFrame>> bus_map; + HashMap<StringName, Vector<AudioFrame>> bus_map; bus_map[_get_actual_bus()] = volume_vector; AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz); stream_playbacks.push_back(new_playback); @@ -387,7 +387,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { Ref<World3D> world_3d = get_world_3d(); ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector); - Set<Camera3D *> cameras = world_3d->get_cameras(); + RBSet<Camera3D *> cameras = world_3d->get_cameras(); cameras.insert(get_viewport()->get_camera_3d()); PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); @@ -466,7 +466,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { output_volume_vector.write[k] = multiplier * output_volume_vector[k]; } - Map<StringName, Vector<AudioFrame>> bus_volumes; + HashMap<StringName, Vector<AudioFrame>> bus_volumes; if (area) { if (area->is_overriding_audio_bus()) { //override audio bus diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 40c09593a4..a36357555a 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -345,9 +345,9 @@ void CollisionObject3D::_update_debug_shapes() { return; } - for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { - if (shapes.has(shapedata_idx->get())) { - ShapeData &shapedata = shapes[shapedata_idx->get()]; + for (const uint32_t &shapedata_idx : debug_shapes_to_update) { + if (shapes.has(shapedata_idx)) { + ShapeData &shapedata = shapes[shapedata_idx]; ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); for (int i = 0; i < shapedata.shapes.size(); i++) { ShapeData::ShapeBase &s = shapes[i]; @@ -459,7 +459,7 @@ void CollisionObject3D::_bind_methods() { ADD_SIGNAL(MethodInfo("mouse_entered")); ADD_SIGNAL(MethodInfo("mouse_exited")); - ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,Make Static,Keep Active"), "set_disable_mode", "get_disable_mode"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index e92843d784..84b00de9c9 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -71,14 +71,14 @@ private: int total_subshapes = 0; - Map<uint32_t, ShapeData> shapes; + RBMap<uint32_t, ShapeData> shapes; bool only_update_transform_changes = false; // This is used for sync to physics. bool capture_input_on_drag = false; bool ray_pickable = true; - Set<uint32_t> debug_shapes_to_update; + RBSet<uint32_t> debug_shapes_to_update; int debug_shapes_count = 0; Transform3D debug_shape_old_transform; diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 9375190151..2d7da48ab1 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -469,8 +469,8 @@ void Label3D::_shape() { aabb = AABB(); // Clear materials. - for (Map<uint64_t, SurfaceData>::Element *E = surfaces.front(); E; E = E->next()) { - RenderingServer::get_singleton()->free(E->get().material); + for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) { + RenderingServer::get_singleton()->free(E.value.material); } surfaces.clear(); @@ -599,20 +599,20 @@ void Label3D::_shape() { offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM)) * pixel_size; } - for (Map<uint64_t, SurfaceData>::Element *E = surfaces.front(); E; E = E->next()) { + for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) { Array mesh_array; mesh_array.resize(RS::ARRAY_MAX); - mesh_array[RS::ARRAY_VERTEX] = E->get().mesh_vertices; - mesh_array[RS::ARRAY_NORMAL] = E->get().mesh_normals; - mesh_array[RS::ARRAY_TANGENT] = E->get().mesh_tangents; - mesh_array[RS::ARRAY_COLOR] = E->get().mesh_colors; - mesh_array[RS::ARRAY_TEX_UV] = E->get().mesh_uvs; - mesh_array[RS::ARRAY_INDEX] = E->get().indices; + mesh_array[RS::ARRAY_VERTEX] = E.value.mesh_vertices; + mesh_array[RS::ARRAY_NORMAL] = E.value.mesh_normals; + mesh_array[RS::ARRAY_TANGENT] = E.value.mesh_tangents; + mesh_array[RS::ARRAY_COLOR] = E.value.mesh_colors; + mesh_array[RS::ARRAY_TEX_UV] = E.value.mesh_uvs; + mesh_array[RS::ARRAY_INDEX] = E.value.indices; RS::SurfaceData sd; RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); - sd.material = E->get().material; + sd.material = E.value.material; RS::get_singleton()->mesh_add_surface(mesh, sd); } @@ -1003,8 +1003,8 @@ Label3D::~Label3D() { TS->free_rid(text_rid); RenderingServer::get_singleton()->free(mesh); - for (Map<uint64_t, SurfaceData>::Element *E = surfaces.front(); E; E = E->next()) { - RenderingServer::get_singleton()->free(E->get().material); + for (KeyValue<uint64_t, SurfaceData> E : surfaces) { + RenderingServer::get_singleton()->free(E.value.material); } surfaces.clear(); } diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 86b8faa617..f57797a247 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -83,7 +83,7 @@ private: RID material; }; - Map<uint64_t, SurfaceData> surfaces; + HashMap<uint64_t, SurfaceData> surfaces; HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER; diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index f641c99ec1..55d9a52a28 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -88,7 +88,9 @@ public: instID(INVALID_GEOMETRY_ID) {} /*! Tests if we hit something. */ - _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + _FORCE_INLINE_ explicit operator bool() const { + return geomID != INVALID_GEOMETRY_ID; + } public: Vector3 org; //!< Ray origin + tnear @@ -116,7 +118,7 @@ public: virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) = 0; virtual void commit() = 0; - virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; + virtual void set_mesh_filter(const RBSet<int> &p_mesh_ids) = 0; virtual void clear_mesh_filter() = 0; static Ref<LightmapRaycaster> create(); diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 31c33a6b61..31993f898d 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -42,9 +42,9 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { return false; } - Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); + HashMap<StringName, int>::Iterator E = blend_shape_properties.find(p_name); if (E) { - set_blend_shape_value(E->get(), p_value); + set_blend_shape_value(E->value, p_value); return true; } @@ -66,9 +66,9 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { return false; } - const Map<StringName, int>::Element *E = blend_shape_properties.find(p_name); + HashMap<StringName, int>::ConstIterator E = blend_shape_properties.find(p_name); if (E) { - r_ret = get_blend_shape_value(E->get()); + r_ret = get_blend_shape_value(E->value); return true; } @@ -97,7 +97,7 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { if (mesh.is_valid()) { for (int i = 0; i < mesh->get_surface_count(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("%s/%d", PNAME("surface_material_override"), i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } } diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 0bf5c32410..dc9c64fa41 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -47,7 +47,7 @@ protected: NodePath skeleton_path = NodePath(".."); LocalVector<float> blend_shape_tracks; - Map<StringName, int> blend_shape_properties; + HashMap<StringName, int> blend_shape_properties; Vector<Ref<Material>> surface_override_materials; void _mesh_changed(); diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 86c11b3789..fb80fe6348 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -113,6 +113,26 @@ void NavigationAgent3D::_notification(int p_what) { set_physics_process_internal(false); } break; + case NOTIFICATION_PAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (agent_parent && !agent_parent->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (agent_parent && agent_parent->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 283b99a24f..6a88bd13e2 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -41,6 +41,7 @@ class NavigationAgent3D : public Node { Node3D *agent_parent = nullptr; RID agent; + RID map_before_pause; uint32_t navigable_layers = 1; diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 78dbecc0c5..c6eda1f9cd 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -35,6 +35,8 @@ #include "servers/navigation_server_3d.h" void NavigationObstacle3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid); + ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius); ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated); ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius); @@ -78,8 +80,28 @@ void NavigationObstacle3D::_notification(int p_what) { parent_node3d = nullptr; } break; + case NOTIFICATION_PAUSED: { + if (parent_node3d && !parent_node3d->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + + case NOTIFICATION_UNPAUSED: { + if (parent_node3d && !parent_node3d->can_process()) { + map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid()); + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) { + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause); + map_before_pause = RID(); + } + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - if (parent_node3d) { + if (parent_node3d && parent_node3d->is_inside_tree()) { NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin); PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); @@ -107,7 +129,12 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.")); + warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object.")); + } + + if (Object::cast_to<StaticBody3D>(get_parent())) { + warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidDynamicBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly." + "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail")); } return warnings; @@ -129,13 +156,13 @@ void NavigationObstacle3D::reevaluate_agent_radius() { } real_t NavigationObstacle3D::estimate_agent_radius() const { - if (parent_node3d) { + if (parent_node3d && parent_node3d->is_inside_tree()) { // Estimate the radius of this physics body real_t radius = 0.0; for (int i(0); i < parent_node3d->get_child_count(); i++) { // For each collision shape CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i)); - if (cs) { + if (cs && cs->is_inside_tree()) { // Take the distance between the Body center to the shape center real_t r = cs->get_transform().origin.length(); if (cs->get_shape().is_valid()) { @@ -146,6 +173,9 @@ real_t NavigationObstacle3D::estimate_agent_radius() const { r *= MAX(s.x, MAX(s.y, s.z)); // Takes the biggest radius radius = MAX(radius, r); + } else if (cs && !cs->is_inside_tree()) { + WARN_PRINT("A CollisionShape3D of the NavigationObstacle3D parent node was not inside the SceneTree when estimating the obstacle radius." + "\nMove the NavigationObstacle3D to a child position below any CollisionShape3D node of the parent node so the CollisionShape3D is already inside the SceneTree."); } } diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index 542d603a0a..0ddde64c0e 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -38,6 +38,7 @@ class NavigationObstacle3D : public Node { Node3D *parent_node3d = nullptr; RID agent; + RID map_before_pause; bool estimate_radius = true; real_t radius = 1.0; diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 80be770dfc..6404432631 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -171,7 +171,12 @@ void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - if (p_on_thread) { + if (p_on_thread && !OS::get_singleton()->can_use_threads()) { + WARN_PRINT("NavigationMesh bake 'on_thread' will be disabled as the current OS does not support multiple threads." + "\nAs a fallback the navigation mesh will bake on the main thread which can cause framerate issues."); + } + + if (p_on_thread && OS::get_singleton()->can_use_threads()) { bake_thread.start(_bake_navigation_mesh, args); } else { _bake_navigation_mesh(args); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index bbc977647e..ad1ba83c9d 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -180,7 +180,7 @@ void Node3D::_notification(int p_what) { #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); } #endif } break; @@ -418,7 +418,7 @@ void Node3D::update_gizmos() { } if (data.gizmos.is_empty()) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); return; } if (data.gizmos_dirty) { @@ -436,7 +436,7 @@ void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transfor } if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform); } #endif } @@ -452,7 +452,7 @@ void Node3D::clear_subgizmo_selection() { } if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); } #endif } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 5f9bca7c49..370ffef19d 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -94,13 +94,11 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p_test_only, real_t p_margin, int p_max_collisions) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); parameters.max_collisions = p_max_collisions; + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. PhysicsServer3D::MotionResult result; - bool collided = move_and_collide(parameters, result, p_test_only); - - // Don't report collision when the whole motion is done. - if (collided && result.collision_safe_fraction < 1) { + if (move_and_collide(parameters, result, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -184,15 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_distan } PhysicsServer3D::MotionParameters parameters(p_from, p_distance, p_margin); + parameters.recovery_as_collision = false; // Don't report collisions generated only from recovery. - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); - - if (colliding) { - // Don't report collision when the whole motion is done. - return (r->collision_safe_fraction < 1.0); - } else { - return false; - } + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -390,18 +382,18 @@ void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) { ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(E->get().in_tree); + ERR_FAIL_COND(E->value.in_tree); - E->get().in_tree = true; + E->value.in_tree = true; contact_monitor->locked = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -412,17 +404,17 @@ void RigidDynamicBody3D::_body_exit_tree(ObjectID p_id) { Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!node); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(p_id); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(p_id); ERR_FAIL_COND(!E); - ERR_FAIL_COND(!E->get().in_tree); - E->get().in_tree = false; + ERR_FAIL_COND(!E->value.in_tree); + E->value.in_tree = false; contact_monitor->locked = true; emit_signal(SceneStringNames::get_singleton()->body_exited, node); - for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + for (int i = 0; i < E->value.shapes.size(); i++) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->value.rid, node, E->value.shapes[i].body_shape, E->value.shapes[i].local_shape); } contact_monitor->locked = false; @@ -436,43 +428,43 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p Node *node = Object::cast_to<Node>(obj); ERR_FAIL_COND(!contact_monitor); - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(objid); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(objid); ERR_FAIL_COND(!body_in && !E); if (body_in) { if (!E) { E = contact_monitor->body_map.insert(objid, BodyState()); - E->get().rid = p_body; - //E->get().rc=0; - E->get().in_tree = node && node->is_inside_tree(); + E->value.rid = p_body; + //E->value.rc=0; + E->value.in_tree = node && node->is_inside_tree(); if (node) { node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree), make_binds(objid)); node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree), make_binds(objid)); - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); } } } - //E->get().rc++; + //E->value.rc++; if (node) { - E->get().shapes.insert(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.insert(ShapePair(p_body_shape, p_local_shape)); } - if (E->get().in_tree) { + if (E->value.in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape); } } else { - //E->get().rc--; + //E->value.rc--; if (node) { - E->get().shapes.erase(ShapePair(p_body_shape, p_local_shape)); + E->value.shapes.erase(ShapePair(p_body_shape, p_local_shape)); } - bool in_tree = E->get().in_tree; + bool in_tree = E->value.in_tree; - if (E->get().shapes.is_empty()) { + if (E->value.shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidDynamicBody3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidDynamicBody3D::_body_exit_tree)); @@ -481,7 +473,7 @@ void RigidDynamicBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p } } - contact_monitor->body_map.erase(E); + contact_monitor->body_map.remove(E); } if (node && in_tree) { emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_local_shape); @@ -547,7 +539,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) //bool found=false; - Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); + HashMap<ObjectID, BodyState>::Iterator E = contact_monitor->body_map.find(obj); if (!E) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -558,7 +550,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) } ShapePair sp(shape, local_shape); - int idx = E->get().shapes.find(sp); + int idx = E->value.shapes.find(sp); if (idx == -1) { toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; @@ -568,7 +560,7 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) continue; } - E->get().shapes[idx].tagged = true; + E->value.shapes[idx].tagged = true; } //put the ones to remove @@ -1214,6 +1206,8 @@ bool CharacterBody3D::move_and_slide() { if (!current_platform_velocity.is_equal_approx(Vector3())) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. + parameters.exclude_bodies.insert(platform_rid); if (platform_object_id.is_valid()) { parameters.exclude_objects.insert(platform_object_id); @@ -1277,6 +1271,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, !sliding_enabled); @@ -1521,6 +1516,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { bool first_slide = true; for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin); + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. PhysicsServer3D::MotionResult result; bool collided = move_and_collide(parameters, result, false, false); @@ -1575,6 +1571,7 @@ void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_ PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -1610,6 +1607,7 @@ bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_f PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin); parameters.max_collisions = 4; + parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.collide_separation_ray = true; PhysicsServer3D::MotionResult result; @@ -2263,9 +2261,9 @@ bool PhysicalBone3D::PinJointData::_get(const StringName &p_name, Variant &r_ret void PhysicalBone3D::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/bias"), PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/damping"), PROPERTY_HINT_RANGE, "0.01,8.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/impulse_clamp"), PROPERTY_HINT_RANGE, "0.0,64.0,0.01")); } bool PhysicalBone3D::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2335,11 +2333,11 @@ bool PhysicalBone3D::ConeJointData::_get(const StringName &p_name, Variant &r_re void PhysicalBone3D::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/swing_span"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/twist_span"), PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/bias"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/relaxation"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); } bool PhysicalBone3D::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2417,12 +2415,12 @@ bool PhysicalBone3D::HingeJointData::_get(const StringName &p_name, Variant &r_r void PhysicalBone3D::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, PNAME("joint_constraints/angular_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_bias"), PROPERTY_HINT_RANGE, "0.01,0.99,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_relaxation"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); } bool PhysicalBone3D::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2532,17 +2530,17 @@ bool PhysicalBone3D::SliderJointData::_get(const StringName &p_name, Variant &r_ void PhysicalBone3D::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const { JointData::_get_property_list(p_list); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_upper")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_lower")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_upper"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_lower"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_restitution"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/linear_limit_damping"), PROPERTY_HINT_RANGE, "0,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_restitution"), PROPERTY_HINT_RANGE, "0.01,16.0,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("joint_constraints/angular_limit_damping"), PROPERTY_HINT_RANGE, "0,16.0,0.01")); } bool PhysicalBone3D::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -2782,29 +2780,30 @@ bool PhysicalBone3D::SixDOFJointData::_get(const StringName &p_name, Variant &r_ } void PhysicalBone3D::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const { - const StringName axis_names[] = { "x", "y", "z" }; + const StringName axis_names[] = { PNAME("x"), PNAME("y"), PNAME("z") }; for (int i = 0; i < 3; ++i) { - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_upper")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_lower")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_spring_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_spring_stiffness")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_spring_damping")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_equilibrium_point")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/erp")); - p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_spring_enabled")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_spring_stiffness")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_spring_damping")); - p_list->push_back(PropertyInfo(Variant::FLOAT, "joint_constraints/" + axis_names[i] + "/angular_equilibrium_point")); + const String prefix = vformat("%s/%s/", PNAME("joint_constraints"), axis_names[i]); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("linear_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_upper"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_lower"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("linear_spring_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_spring_stiffness"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_spring_damping"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_equilibrium_point"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_restitution"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("linear_damping"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("angular_limit_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_upper"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_lower"), PROPERTY_HINT_RANGE, "-180,180,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_limit_softness"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_restitution"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_damping"), PROPERTY_HINT_RANGE, "0.01,16,0.01")); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("erp"))); + p_list->push_back(PropertyInfo(Variant::BOOL, prefix + PNAME("angular_spring_enabled"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_spring_stiffness"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_spring_damping"))); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix + PNAME("angular_equilibrium_point"))); } } @@ -2851,9 +2850,9 @@ void PhysicalBone3D::_get_property_list(List<PropertyInfo> *p_list) const { names += parent->get_bone_name(i); } - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_name", PROPERTY_HINT_ENUM, names)); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"), PROPERTY_HINT_ENUM, names)); } else { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bone_name")); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, PNAME("bone_name"))); } if (joint_data) { diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index e64987b73e..22dcb218bc 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -212,7 +212,7 @@ private: struct ContactMonitor { bool locked = false; - Map<ObjectID, BodyState> body_map; + HashMap<ObjectID, BodyState> body_map; }; ContactMonitor *contact_monitor = nullptr; @@ -714,7 +714,9 @@ public: const JointData *get_joint_data() const; Skeleton3D *find_skeleton_parent(); - int get_bone_id() const { return bone_id; } + int get_bone_id() const { + return bone_id; + } void set_joint_type(JointType p_joint_type); JointType get_joint_type() const; diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index ad85001591..d6062969d8 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -46,7 +46,7 @@ class RayCast3D : public Node3D { Vector3 collision_normal; Vector3 target_position = Vector3(0, -1, 0); - Set<RID> exclude; + RBSet<RID> exclude; uint32_t collision_mask = 1; bool exclude_parent_body = true; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 9e403a6ecd..ba2029788e 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -153,14 +153,14 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { for (int i = 0; i < bones.size(); i++) { - String prep = "bones/" + itos(i) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + const String prep = vformat("%s/%d/", PNAME("bones"), i); + p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } #ifndef _3D_DISABLED @@ -263,19 +263,19 @@ void Skeleton3D::_notification(int p_what) { force_update_all_bone_transforms(); // Update skins. - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - const Skin *skin = E->get()->skin.operator->(); - RID skeleton = E->get()->skeleton; + for (SkinReference *E : skin_bindings) { + const Skin *skin = E->skin.operator->(); + RID skeleton = E->skeleton; uint32_t bind_count = skin->get_bind_count(); - if (E->get()->bind_count != bind_count) { + if (E->bind_count != bind_count) { RS::get_singleton()->skeleton_allocate_data(skeleton, bind_count); - E->get()->bind_count = bind_count; - E->get()->skin_bone_indices.resize(bind_count); - E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); + E->bind_count = bind_count; + E->skin_bone_indices.resize(bind_count); + E->skin_bone_indices_ptrs = E->skin_bone_indices.ptrw(); } - if (E->get()->skeleton_version != version) { + if (E->skeleton_version != version) { for (uint32_t i = 0; i < bind_count; i++) { StringName bind_name = skin->get_bind_name(i); @@ -284,7 +284,7 @@ void Skeleton3D::_notification(int p_what) { bool found = false; for (int j = 0; j < len; j++) { if (bonesptr[j].name == bind_name) { - E->get()->skin_bone_indices_ptrs[i] = j; + E->skin_bone_indices_ptrs[i] = j; found = true; break; } @@ -292,27 +292,27 @@ void Skeleton3D::_notification(int p_what) { if (!found) { ERR_PRINT("Skin bind #" + itos(i) + " contains named bind '" + String(bind_name) + "' but Skeleton3D has no bone by that name."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } } else if (skin->get_bind_bone(i) >= 0) { int bind_index = skin->get_bind_bone(i); if (bind_index >= len) { ERR_PRINT("Skin bind #" + itos(i) + " contains bone index bind: " + itos(bind_index) + " , which is greater than the skeleton bone count: " + itos(len) + "."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } else { - E->get()->skin_bone_indices_ptrs[i] = bind_index; + E->skin_bone_indices_ptrs[i] = bind_index; } } else { ERR_PRINT("Skin bind #" + itos(i) + " does not contain a name nor a bone index."); - E->get()->skin_bone_indices_ptrs[i] = 0; + E->skin_bone_indices_ptrs[i] = 0; } } - E->get()->skeleton_version = version; + E->skeleton_version = version; } for (uint32_t i = 0; i < bind_count; i++) { - uint32_t bone_index = E->get()->skin_bone_indices_ptrs[i]; + uint32_t bone_index = E->skin_bone_indices_ptrs[i]; ERR_CONTINUE(bone_index >= (uint32_t)len); rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); } @@ -1000,9 +1000,9 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() { Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>()); - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - if (E->get()->skin == p_skin) { - return Ref<SkinReference>(E->get()); + for (const SkinReference *E : skin_bindings) { + if (E->skin == p_skin) { + return Ref<SkinReference>(E); } } @@ -1303,7 +1303,7 @@ Skeleton3D::Skeleton3D() { Skeleton3D::~Skeleton3D() { // Some skins may remain bound. - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { - E->get()->skeleton_node = nullptr; + for (SkinReference *E : skin_bindings) { + E->skeleton_node = nullptr; } } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index ca706bea98..c72792bd47 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -131,7 +131,7 @@ private: } }; - Set<SkinReference *> skin_bindings; + RBSet<SkinReference *> skin_bindings; void _skin_changed(); diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp index 7d786a41bf..5816c0650f 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -162,12 +162,13 @@ bool SoftDynamicBody3D::_get(const StringName &p_name, Variant &r_ret) const { void SoftDynamicBody3D::_get_property_list(List<PropertyInfo> *p_list) const { const int pinned_points_indices_size = pinned_points.size(); - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, "pinned_points")); + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, PNAME("pinned_points"))); for (int i = 0; i < pinned_points_indices_size; ++i) { - p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); - p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); - p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); + const String prefix = vformat("%s/%d", PNAME("attachments"), i); + p_list->push_back(PropertyInfo(Variant::INT, prefix + PNAME("point_index"))); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, prefix + PNAME("spatial_attachment_path"))); + p_list->push_back(PropertyInfo(Variant::VECTOR3, prefix + PNAME("offset"))); } } diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h index b247ea1707..78d9db7259 100644 --- a/scene/3d/spring_arm_3d.h +++ b/scene/3d/spring_arm_3d.h @@ -37,7 +37,7 @@ class SpringArm3D : public Node3D { GDCLASS(SpringArm3D, Node3D); Ref<Shape3D> shape; - Set<RID> excluded_objects; + RBSet<RID> excluded_objects; real_t spring_length = 1.0; real_t current_spring_length = 0.0; bool keep_child_basis = false; diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index d2371d819b..4ef70f7764 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -162,7 +162,7 @@ class VehicleBody3D : public RigidDynamicBody3D { real_t m_steeringValue = 0.0; real_t m_currentVehicleSpeedKmHour = 0.0; - Set<RID> exclude; + RBSet<RID> exclude; Vector<Vector3> m_forwardWS; Vector<Vector3> m_axle; diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index dc7569d17c..0179795ddc 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -86,7 +86,7 @@ private: Vector<Color> emission; }; - Map<Ref<Material>, MaterialCache> material_cache; + HashMap<Ref<Material>, MaterialCache> material_cache; AABB original_bounds; AABB po2_bounds; int axis_cell_size[3] = {}; diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 849316c568..594f98410e 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -219,14 +219,14 @@ void AnimationNodeBlendSpace1D::_add_blend_point(int p_index, const Ref<Animatio } } -double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) { +double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_seek_root) { if (blend_points_used == 0) { return 0.0; } if (blend_points_used == 1) { // only one point available, just play that animation - return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + return blend_node(blend_points[0].name, blend_points[0].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); } double blend_pos = get_parameter(blend_position); @@ -295,7 +295,7 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek) { double max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, weights[i], FILTER_IGNORE, false); max_time_remaining = MAX(max_time_remaining, remaining); } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 7038cece06..b2075c8c93 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -93,7 +93,7 @@ public: void set_value_label(const String &p_label); String get_value_label() const; - double process(double p_time, bool p_seek) override; + double process(double p_time, bool p_seek, bool p_seek_root) override; String get_caption() const override; Ref<AnimationNode> get_child_by_name(const StringName &p_name) override; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index a3aa3f6cc8..acdce2d7de 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -432,7 +432,7 @@ void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vect r_weights[2] = w; } -double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { +double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek, bool p_seek_root) { _update_triangles(); Vector2 blend_pos = get_parameter(blend_position); @@ -502,7 +502,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { for (int j = 0; j < 3; j++) { if (i == triangle_points[j]) { //blend with the given weight - double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); + double t = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, blend_weights[j], FILTER_IGNORE, false); if (first || t < mind) { mind = t; first = false; @@ -514,7 +514,7 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { if (!found) { //ignore - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_seek_root, 0, FILTER_IGNORE, false); } } } else { @@ -539,16 +539,16 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) { na_n->set_backward(na_c->is_backward()); } //see how much animation remains - from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false); + from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, p_seek_root, 0.0, FILTER_IGNORE, false); } - mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, 1.0, FILTER_IGNORE, false); + mind = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_seek_root, 1.0, FILTER_IGNORE, false); length_internal = from + mind; closest = new_closest; } else { - mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + mind = blend_node(blend_points[closest].name, blend_points[closest].node, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); } } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 1356656bf8..01f53ed25a 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -126,7 +126,7 @@ public: void set_y_label(const String &p_label); String get_y_label() const; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; virtual String get_caption() const override; Vector2 get_closest_point(const Vector2 &p_point); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 433f21f91f..17a99ed034 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -64,7 +64,7 @@ void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { } } -double AnimationNodeAnimation::process(double p_time, bool p_seek) { +double AnimationNodeAnimation::process(double p_time, bool p_seek, bool p_seek_root) { AnimationPlayer *ap = state->player; ERR_FAIL_COND_V(!ap, 0); @@ -101,8 +101,8 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) { } } - if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) { - if (anim_size) { + if (anim->get_loop_mode() == Animation::LOOP_PINGPONG) { + if (!Math::is_zero_approx(anim_size)) { if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) { if (prev_time > 0 && time <= 0) { backward = !backward; @@ -116,22 +116,24 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) { time = Math::pingpong(time, anim_size); } } else { - if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) { - if (anim_size) { + if (anim->get_loop_mode() == Animation::LOOP_LINEAR) { + if (!Math::is_zero_approx(anim_size)) { time = Math::fposmod(time, anim_size); } } else if (time < 0) { + step += time; time = 0; } else if (time > anim_size) { + step += anim_size - time; time = anim_size; } backward = false; } if (play_mode == PLAY_MODE_FORWARD) { - blend_animation(animation, time, step, p_seek, 1.0, pingponged); + blend_animation(animation, time, step, p_seek, p_seek_root, 1.0, pingponged); } else { - blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged); + blend_animation(animation, anim_size - time, -step, p_seek, p_seek_root, 1.0, pingponged); } set_parameter(this->time, time); @@ -251,7 +253,7 @@ bool AnimationNodeOneShot::has_filter() const { return true; } -double AnimationNodeOneShot::process(double p_time, bool p_seek) { +double AnimationNodeOneShot::process(double p_time, bool p_seek, bool p_seek_root) { bool active = get_parameter(this->active); bool prev_active = get_parameter(this->prev_active); double time = get_parameter(this->time); @@ -274,7 +276,7 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) { } if (!active) { - return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); + return blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); } } @@ -311,12 +313,12 @@ double AnimationNodeOneShot::process(double p_time, bool p_seek) { double main_rem; if (mix == MIX_MODE_ADD) { - main_rem = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); + main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); } else { - main_rem = blend_input(0, p_time, p_seek, 1.0 - blend, FILTER_BLEND, !sync); + main_rem = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_BLEND, !sync); } - double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, blend, FILTER_PASS, false); + double os_rem = blend_input(1, os_seek ? time : p_time, os_seek, p_seek_root, blend, FILTER_PASS, false); if (do_start) { remaining = os_rem; @@ -420,10 +422,10 @@ bool AnimationNodeAdd2::has_filter() const { return true; } -double AnimationNodeAdd2::process(double p_time, bool p_seek) { +double AnimationNodeAdd2::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(add_amount); - double rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); - blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); + blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync); return rem0; } @@ -466,11 +468,11 @@ bool AnimationNodeAdd3::has_filter() const { return true; } -double AnimationNodeAdd3::process(double p_time, bool p_seek) { +double AnimationNodeAdd3::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(add_amount); - blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync); - double rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); - blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync); + blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_PASS, !sync); + double rem0 = blend_input(1, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, !sync); + blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_PASS, !sync); return rem0; } @@ -502,11 +504,11 @@ String AnimationNodeBlend2::get_caption() const { return "Blend2"; } -double AnimationNodeBlend2::process(double p_time, bool p_seek) { +double AnimationNodeBlend2::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(blend_amount); - double rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync); - double rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, 1.0 - amount, FILTER_BLEND, !sync); + double rem1 = blend_input(1, p_time, p_seek, p_seek_root, amount, FILTER_PASS, !sync); return amount > 0.5 ? rem1 : rem0; //hacky but good enough } @@ -557,11 +559,11 @@ bool AnimationNodeBlend3::is_using_sync() const { return sync; } -double AnimationNodeBlend3::process(double p_time, bool p_seek) { +double AnimationNodeBlend3::process(double p_time, bool p_seek, bool p_seek_root) { double amount = get_parameter(blend_amount); - double rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync); - double rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync); - double rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync); + double rem0 = blend_input(0, p_time, p_seek, p_seek_root, MAX(0, -amount), FILTER_IGNORE, !sync); + double rem1 = blend_input(1, p_time, p_seek, p_seek_root, 1.0 - ABS(amount), FILTER_IGNORE, !sync); + double rem2 = blend_input(2, p_time, p_seek, p_seek_root, MAX(0, amount), FILTER_IGNORE, !sync); return amount > 0.5 ? rem2 : (amount < -0.5 ? rem0 : rem1); //hacky but good enough } @@ -574,7 +576,6 @@ void AnimationNodeBlend3::_bind_methods() { } AnimationNodeBlend3::AnimationNodeBlend3() { - blend_amount = "blend_amount"; add_input("-blend"); add_input("in"); add_input("+blend"); @@ -595,12 +596,12 @@ String AnimationNodeTimeScale::get_caption() const { return "TimeScale"; } -double AnimationNodeTimeScale::process(double p_time, bool p_seek) { +double AnimationNodeTimeScale::process(double p_time, bool p_seek, bool p_seek_root) { double scale = get_parameter(this->scale); if (p_seek) { - return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false); } else { - return blend_input(0, p_time * scale, false, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time * scale, false, p_seek_root, 1.0, FILTER_IGNORE, false); } } @@ -625,16 +626,16 @@ String AnimationNodeTimeSeek::get_caption() const { return "Seek"; } -double AnimationNodeTimeSeek::process(double p_time, bool p_seek) { +double AnimationNodeTimeSeek::process(double p_time, bool p_seek, bool p_seek_root) { double seek_pos = get_parameter(this->seek_pos); if (p_seek) { - return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, true, p_seek_root, 1.0, FILTER_IGNORE, false); } else if (seek_pos >= 0) { - double ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false); + double ret = blend_input(0, seek_pos, true, true, 1.0, FILTER_IGNORE, false); set_parameter(this->seek_pos, -1.0); //reset return ret; } else { - return blend_input(0, p_time, false, 1.0, FILTER_IGNORE, false); + return blend_input(0, p_time, false, p_seek_root, 1.0, FILTER_IGNORE, false); } } @@ -726,7 +727,7 @@ float AnimationNodeTransition::get_cross_fade_time() const { return xfade; } -double AnimationNodeTransition::process(double p_time, bool p_seek) { +double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_root) { int current = get_parameter(this->current); int prev = get_parameter(this->prev); int prev_current = get_parameter(this->prev_current); @@ -754,7 +755,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) { if (prev < 0) { // process current animation, check for transition - rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0, FILTER_IGNORE, false); if (p_seek) { time = p_time; @@ -772,16 +773,16 @@ double AnimationNodeTransition::process(double p_time, bool p_seek) { if (!p_seek && switched) { //just switched, seek to start of current - rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false); + rem = blend_input(current, 0, true, p_seek_root, 1.0 - blend, FILTER_IGNORE, false); } else { - rem = blend_input(current, p_time, p_seek, 1.0 - blend, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, p_seek_root, 1.0 - blend, FILTER_IGNORE, false); } if (p_seek) { // don't seek prev animation - blend_input(prev, 0, false, blend, FILTER_IGNORE, false); + blend_input(prev, 0, false, p_seek_root, blend, FILTER_IGNORE, false); time = p_time; } else { - blend_input(prev, p_time, false, blend, FILTER_IGNORE, false); + blend_input(prev, p_time, false, p_seek_root, blend, FILTER_IGNORE, false); time += p_time; prev_xfading -= p_time; if (prev_xfading < 0) { @@ -844,8 +845,8 @@ String AnimationNodeOutput::get_caption() const { return "Output"; } -double AnimationNodeOutput::process(double p_time, bool p_seek) { - return blend_input(0, p_time, p_seek, 1.0); +double AnimationNodeOutput::process(double p_time, bool p_seek, bool p_seek_root) { + return blend_input(0, p_time, p_seek, p_seek_root, 1.0); } AnimationNodeOutput::AnimationNodeOutput() { @@ -1057,9 +1058,9 @@ String AnimationNodeBlendTree::get_caption() const { return "BlendTree"; } -double AnimationNodeBlendTree::process(double p_time, bool p_seek) { +double AnimationNodeBlendTree::process(double p_time, bool p_seek, bool p_seek_root) { Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node; - return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0); + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, p_seek_root, 1.0); } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 2acacd7396..0a2305b8d6 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -53,7 +53,7 @@ public: static Vector<String> (*get_editable_animation_list)(); virtual String get_caption() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; void set_animation(const StringName &p_name); StringName get_animation() const; @@ -87,8 +87,8 @@ public: }; private: - float fade_in = 0.1; - float fade_out = 0.1; + float fade_in = 0.0; + float fade_out = 0.0; bool autorestart = false; float autorestart_delay = 1.0; @@ -102,7 +102,7 @@ private: float time; float remaining;*/ - StringName active = "active"; + StringName active = PNAME("active"); StringName prev_active = "prev_active"; StringName time = "time"; StringName remaining = "remaining"; @@ -138,7 +138,7 @@ public: bool is_using_sync() const; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeOneShot(); }; @@ -148,7 +148,7 @@ VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNode { GDCLASS(AnimationNodeAdd2, AnimationNode); - StringName add_amount = "add_amount"; + StringName add_amount = PNAME("add_amount"); bool sync = false; protected: @@ -164,7 +164,7 @@ public: bool is_using_sync() const; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeAdd2(); }; @@ -172,7 +172,7 @@ public: class AnimationNodeAdd3 : public AnimationNode { GDCLASS(AnimationNodeAdd3, AnimationNode); - StringName add_amount = "add_amount"; + StringName add_amount = PNAME("add_amount"); bool sync = false; protected: @@ -188,7 +188,7 @@ public: bool is_using_sync() const; virtual bool has_filter() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeAdd3(); }; @@ -196,7 +196,7 @@ public: class AnimationNodeBlend2 : public AnimationNode { GDCLASS(AnimationNodeBlend2, AnimationNode); - StringName blend_amount = "blend_amount"; + StringName blend_amount = PNAME("blend_amount"); bool sync = false; protected: @@ -207,7 +207,7 @@ public: virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; virtual String get_caption() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -219,7 +219,7 @@ public: class AnimationNodeBlend3 : public AnimationNode { GDCLASS(AnimationNodeBlend3, AnimationNode); - StringName blend_amount; + StringName blend_amount = PNAME("blend_amount"); bool sync; protected: @@ -234,14 +234,14 @@ public: void set_use_sync(bool p_sync); bool is_using_sync() const; - double process(double p_time, bool p_seek) override; + double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeBlend3(); }; class AnimationNodeTimeScale : public AnimationNode { GDCLASS(AnimationNodeTimeScale, AnimationNode); - StringName scale = "scale"; + StringName scale = PNAME("scale"); protected: static void _bind_methods(); @@ -252,7 +252,7 @@ public: virtual String get_caption() const override; - double process(double p_time, bool p_seek) override; + double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeTimeScale(); }; @@ -260,7 +260,7 @@ public: class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); - StringName seek_pos = "seek_position"; + StringName seek_pos = PNAME("seek_position"); protected: static void _bind_methods(); @@ -271,7 +271,7 @@ public: virtual String get_caption() const override; - double process(double p_time, bool p_seek) override; + double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeTimeSeek(); }; @@ -300,7 +300,7 @@ class AnimationNodeTransition : public AnimationNode { StringName prev_xfading = "prev_xfading"; StringName prev = "prev"; StringName time = "time"; - StringName current = "current"; + StringName current = PNAME("current"); StringName prev_current = "prev_current"; float xfade = 0.0; @@ -329,7 +329,7 @@ public: void set_cross_fade_time(float p_fade); float get_cross_fade_time() const; - double process(double p_time, bool p_seek) override; + double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeTransition(); }; @@ -339,7 +339,7 @@ class AnimationNodeOutput : public AnimationNode { public: virtual String get_caption() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; AnimationNodeOutput(); }; @@ -354,7 +354,7 @@ class AnimationNodeBlendTree : public AnimationRootNode { Vector<StringName> connections; }; - Map<StringName, Node> nodes; + HashMap<StringName, Node> nodes; Vector2 graph_offset; @@ -408,7 +408,7 @@ public: void get_node_connections(List<NodeConnection> *r_connections) const; virtual String get_caption() const override; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; void get_node_list(List<StringName> *r_list); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index b3cae4f5b5..39849a0b00 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -188,7 +188,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta Vector2 current_pos = p_state_machine->states[current].position; Vector2 target_pos = p_state_machine->states[p_travel].position; - Map<StringName, AStarCost> cost_map; + HashMap<StringName, AStarCost> cost_map; List<int> open_list; @@ -292,7 +292,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta return true; } -double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek) { +double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) { //if not playing and it can restart, then restart if (!playing && start_request == StringName()) { if (!stop_request && p_state_machine->start_node) { @@ -356,7 +356,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s current = p_state_machine->start_node; } - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -381,10 +381,10 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } } - float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false); + float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, false); if (fading_from != StringName()) { - p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); + p_state_machine->blend_node(fading_from, p_state_machine->states[fading_from].node, p_time, p_seek, p_seek_root, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); } //guess playback position @@ -538,12 +538,12 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); pos_current = MIN(pos_current, len_current); - p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false); + p_state_machine->blend_node(current, p_state_machine->states[current].node, pos_current, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); } else { - len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); + len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -1071,11 +1071,11 @@ Vector2 AnimationNodeStateMachine::get_graph_offset() const { return graph_offset; } -double AnimationNodeStateMachine::process(double p_time, bool p_seek) { +double AnimationNodeStateMachine::process(double p_time, bool p_seek, bool p_seek_root) { Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback); ERR_FAIL_COND_V(playback.is_null(), 0.0); - return playback->process(this, p_time, p_seek); + return playback->process(this, p_time, p_seek, p_seek_root); } String AnimationNodeStateMachine::get_caption() const { diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 39a84358fb..9eeac6a183 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -120,7 +120,7 @@ class AnimationNodeStateMachinePlayback : public Resource { bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); - double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek); + double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root); bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; @@ -152,7 +152,7 @@ private: Vector2 position; }; - Map<StringName, State> states; + HashMap<StringName, State> states; struct Transition { StringName from; @@ -226,7 +226,7 @@ public: void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; - virtual double process(double p_time, bool p_seek) override; + virtual double process(double p_time, bool p_seek, bool p_seek_root) override; virtual String get_caption() const override; virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name) override; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 081e6e809a..921a06b73c 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -163,7 +163,7 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { for (int i = 0; i < keys.size(); i++) { array.push_back(keys[i].from); array.push_back(keys[i].to); - array.push_back(blend_times[keys[i]]); + array.push_back(blend_times.get(keys[i])); } r_ret = array; @@ -588,10 +588,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double //StringName property=a->track_get_path(i).get_property(); - Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames()); + HashMap<StringName, TrackNodeCache::PropertyAnim>::Iterator E = nc->property_anim.find(a->track_get_path(i).get_concatenated_subnames()); ERR_CONTINUE(!E); //should it continue, or create a new one? - TrackNodeCache::PropertyAnim *pa = &E->get(); + TrackNodeCache::PropertyAnim *pa = &E->value; Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); @@ -738,10 +738,10 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double continue; } - Map<StringName, TrackNodeCache::BezierAnim>::Element *E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames()); + HashMap<StringName, TrackNodeCache::BezierAnim>::Iterator E = nc->bezier_anim.find(a->track_get_path(i).get_concatenated_subnames()); ERR_CONTINUE(!E); //should it continue, or create a new one? - TrackNodeCache::BezierAnim *ba = &E->get(); + TrackNodeCache::BezierAnim *ba = &E->value; real_t bezier = a->bezier_track_interpolate(i, p_time); if (ba->accum_pass != accum_pass) { @@ -832,7 +832,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double nc->audio_start = p_time; } } else if (nc->audio_playing) { - bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE; + bool loop = a->get_loop_mode() != Animation::LOOP_NONE; bool stop = false; @@ -883,15 +883,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double double at_anim_pos = 0.0; switch (anim->get_loop_mode()) { - case Animation::LoopMode::LOOP_NONE: { + case Animation::LOOP_NONE: { at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end } break; - case Animation::LoopMode::LOOP_LINEAR: { + case Animation::LOOP_LINEAR: { at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop } break; - case Animation::LoopMode::LOOP_PINGPONG: { + case Animation::LOOP_PINGPONG: { at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length()); } break; @@ -944,7 +944,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, int pingponged = 0; switch (cd.from->animation->get_loop_mode()) { - case Animation::LoopMode::LOOP_NONE: { + case Animation::LOOP_NONE: { if (next_pos < 0) { next_pos = 0; } else if (next_pos > len) { @@ -969,7 +969,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, } } break; - case Animation::LoopMode::LOOP_LINEAR: { + case Animation::LOOP_LINEAR: { double looped_next_pos = Math::fposmod(next_pos, (double)len); if (looped_next_pos == 0 && next_pos != 0) { // Loop multiples of the length to it, rather than 0 @@ -980,7 +980,7 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta, } } break; - case Animation::LoopMode::LOOP_PINGPONG: { + case Animation::LOOP_PINGPONG: { if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) { if (next_pos < 0 && cd.pos >= 0) { cd.speed_scale *= -1.0; @@ -1272,7 +1272,7 @@ void AnimationPlayer::_animation_removed(const StringName &p_name, const StringN void AnimationPlayer::_rename_animation(const StringName &p_from_name, const StringName &p_to_name) { // Rename autoplay or blends if needed. List<BlendKey> to_erase; - Map<BlendKey, float> to_insert; + HashMap<BlendKey, float, BlendKey> to_insert; for (const KeyValue<BlendKey, float> &E : blend_times) { BlendKey bk = E.key; BlendKey new_bk = bk; @@ -1298,8 +1298,8 @@ void AnimationPlayer::_rename_animation(const StringName &p_from_name, const Str } while (to_insert.size()) { - blend_times[to_insert.front()->key()] = to_insert.front()->get(); - to_insert.erase(to_insert.front()); + blend_times[to_insert.begin()->key] = to_insert.begin()->value; + to_insert.remove(to_insert.begin()); } if (autoplay == p_from_name) { @@ -1766,12 +1766,12 @@ void AnimationPlayer::_animation_changed() { } void AnimationPlayer::_stop_playing_caches() { - for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) { - if (E->get()->node && E->get()->audio_playing) { - E->get()->node->call(SNAME("stop")); + for (TrackNodeCache *E : playing_caches) { + if (E->node && E->audio_playing) { + E->node->call(SNAME("stop")); } - if (E->get()->node && E->get()->animation_playing) { - AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->get()->node); + if (E->node && E->animation_playing) { + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(E->node); if (!player) { continue; } diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 8c2f0e390b..3ef87dba28 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -132,7 +132,7 @@ private: Variant capture; }; - Map<StringName, PropertyAnim> property_anim; + HashMap<StringName, PropertyAnim> property_anim; struct BezierAnim { Vector<StringName> bezier_property; @@ -142,7 +142,7 @@ private: uint64_t accum_pass = 0; }; - Map<StringName, BezierAnim> bezier_anim; + HashMap<StringName, BezierAnim> bezier_anim; uint32_t last_setup_pass = 0; TrackNodeCache() {} @@ -153,6 +153,16 @@ private: int bone_idx = -1; int blend_shape_idx = -1; + static uint32_t hash(const TrackNodeCacheKey &p_key) { + uint32_t h = hash_one_uint64(p_key.id); + h = hash_djb2_one_32(p_key.bone_idx, h); + return hash_djb2_one_32(p_key.blend_shape_idx, h); + } + + inline bool operator==(const TrackNodeCacheKey &p_right) const { + return id == p_right.id && bone_idx == p_right.bone_idx && blend_shape_idx == p_right.blend_shape_idx; + } + inline bool operator<(const TrackNodeCacheKey &p_right) const { if (id == p_right.id) { if (blend_shape_idx == p_right.blend_shape_idx) { @@ -166,7 +176,7 @@ private: } }; - Map<TrackNodeCacheKey, TrackNodeCache> node_cache_map; + HashMap<TrackNodeCacheKey, TrackNodeCache, TrackNodeCacheKey> node_cache_map; TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX]; int cache_update_size = 0; @@ -174,7 +184,7 @@ private: int cache_update_prop_size = 0; TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX]; int cache_update_bezier_size = 0; - Set<TrackNodeCache *> playing_caches; + RBSet<TrackNodeCache *> playing_caches; uint64_t accum_pass = 1; float speed_scale = 1.0; @@ -189,7 +199,7 @@ private: uint64_t last_update = 0; }; - Map<StringName, AnimationData> animation_set; + HashMap<StringName, AnimationData> animation_set; struct AnimationLibraryData { StringName name; @@ -202,10 +212,22 @@ private: struct BlendKey { StringName from; StringName to; - bool operator<(const BlendKey &bk) const { return from == bk.from ? String(to) < String(bk.to) : String(from) < String(bk.from); } + static uint32_t hash(const BlendKey &p_key) { + return hash_one_uint64((uint64_t(p_key.from.hash()) << 32) | uint32_t(p_key.to.hash())); + } + bool operator==(const BlendKey &bk) const { + return from == bk.from && to == bk.to; + } + bool operator<(const BlendKey &bk) const { + if (from == bk.from) { + return to < bk.to; + } else { + return from < bk.from; + } + } }; - Map<BlendKey, float> blend_times; + HashMap<BlendKey, float, BlendKey> blend_times; struct PlaybackData { AnimationData *from = nullptr; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index d957c7f2de..d34e8db093 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -88,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { } } -void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged) { +void AnimationNode::blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged) { ERR_FAIL_COND(!state); ERR_FAIL_COND(!state->player->has_animation(p_animation)); @@ -115,17 +115,18 @@ void AnimationNode::blend_animation(const StringName &p_animation, double p_time anim_state.animation = animation; anim_state.seeked = p_seeked; anim_state.pingponged = p_pingponged; + anim_state.seek_root = p_seek_root; state->animation_states.push_back(anim_state); } -double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections) { +double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections) { base_path = p_base_path; parent = p_parent; connections = p_connections; state = p_state; - double t = process(p_time, p_seek); + double t = process(p_time, p_seek, p_seek_root); state = nullptr; parent = nullptr; @@ -144,7 +145,7 @@ void AnimationNode::make_invalid(const String &p_reason) { state->invalid_reasons += String::utf8("• ") + p_reason; } -double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) { +double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); @@ -163,7 +164,7 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_ //inputs.write[p_input].last_pass = state->last_pass; real_t activity = 0.0; - double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); + double ret = _blend_node(node_name, blend_tree->get_node_connection_array(node_name), nullptr, node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize, &activity); Vector<AnimationTree::Activity> *activity_ptr = state->tree->input_activity_map.getptr(base_path); @@ -174,11 +175,11 @@ double AnimationNode::blend_input(int p_input, double p_time, bool p_seek, real_ return ret; } -double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize) { - return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_blend, p_filter, p_optimize); +double AnimationNode::blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize) { + return _blend_node(p_sub_path, Vector<StringName>(), this, p_node, p_time, p_seek, p_seek_root, p_blend, p_filter, p_optimize); } -double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) { +double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter, bool p_optimize, real_t *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -286,9 +287,9 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri } if (!p_seek && p_optimize && !any_valid) { - return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_connections); + return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections); } - return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_connections); + return p_node->_pre_process(new_path, new_parent, state, p_time, p_seek, p_seek_root, p_connections); } int AnimationNode::get_input_count() const { @@ -332,9 +333,9 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } -double AnimationNode::process(double p_time, bool p_seek) { +double AnimationNode::process(double p_time, bool p_seek, bool p_seek_root) { double ret; - if (GDVIRTUAL_CALL(_process, p_time, p_seek, ret)) { + if (GDVIRTUAL_CALL(_process, p_time, p_seek, p_seek_root, ret)) { return ret; } @@ -418,9 +419,9 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); - ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "seek_root", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "seek_root", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_parameter", "name", "value"), &AnimationNode::set_parameter); ClassDB::bind_method(D_METHOD("get_parameter", "name"), &AnimationNode::get_parameter); @@ -432,7 +433,7 @@ void AnimationNode::_bind_methods() { GDVIRTUAL_BIND(_get_parameter_list); GDVIRTUAL_BIND(_get_child_by_name, "name"); GDVIRTUAL_BIND(_get_parameter_default_value, "parameter"); - GDVIRTUAL_BIND(_process, "time", "seek"); + GDVIRTUAL_BIND(_process, "time", "seek", "seek_root"); GDVIRTUAL_BIND(_get_caption); GDVIRTUAL_BIND(_has_filter); @@ -486,9 +487,9 @@ void AnimationTree::set_active(bool p_active) { } if (!active && is_inside_tree()) { - for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) { - if (ObjectDB::get_instance(E->get()->object_id)) { - E->get()->object->call(SNAME("stop")); + for (const TrackCache *E : playing_caches) { + if (ObjectDB::get_instance(E->object_id)) { + E->object->call(SNAME("stop")); } } @@ -859,7 +860,6 @@ void AnimationTree::_process_graph(double p_delta) { _update_properties(); //if properties need updating, update them //check all tracks, see if they need modification - root_motion_transform = Transform3D(); if (!root.is_valid()) { @@ -938,11 +938,11 @@ void AnimationTree::_process_graph(double p_delta) { { if (started) { //if started, seek - root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, Vector<StringName>()); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, 0, true, false, Vector<StringName>()); started = false; } - root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, Vector<StringName>()); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, nullptr, &state, p_delta, false, false, Vector<StringName>()); } if (!state.valid) { @@ -962,6 +962,7 @@ void AnimationTree::_process_graph(double p_delta) { int pingponged = as.pingponged; #ifndef _3D_DISABLED bool backward = signbit(delta); + bool calc_root = !seeked || as.seek_root; #endif // _3D_DISABLED for (int i = 0; i < a->get_track_count(); i++) { @@ -990,7 +991,7 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (track->root_motion) { + if (track->root_motion && calc_root) { if (t->process_pass != process_pass) { t->process_pass = process_pass; t->loc = Vector3(0, 0, 0); @@ -1052,7 +1053,7 @@ void AnimationTree::_process_graph(double p_delta) { } a->position_track_interpolate(i, 0, &loc[1]); t->loc += (loc[1] - loc[0]) * blend; - prev_time = 0; + prev_time = (double)a->get_length(); } } @@ -1086,7 +1087,7 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_ROTATION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (track->root_motion) { + if (track->root_motion && calc_root) { if (t->process_pass != process_pass) { t->process_pass = process_pass; t->loc = Vector3(0, 0, 0); @@ -1148,7 +1149,7 @@ void AnimationTree::_process_graph(double p_delta) { } a->rotation_track_interpolate(i, 0, &rot[1]); t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); - prev_time = 0; + prev_time = (double)a->get_length(); } } @@ -1182,7 +1183,7 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (track->root_motion) { + if (track->root_motion && calc_root) { if (t->process_pass != process_pass) { t->process_pass = process_pass; t->loc = Vector3(0, 0, 0); @@ -1244,7 +1245,7 @@ void AnimationTree::_process_graph(double p_delta) { } a->scale_track_interpolate(i, 0, &scale[1]); t->scale += (scale[1] - scale[0]) * blend; - prev_time = 0; + prev_time = (double)a->get_length(); } } @@ -1301,8 +1302,7 @@ void AnimationTree::_process_graph(double p_delta) { Animation::UpdateMode update_mode = a->value_track_get_update_mode(i); - if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek - + if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { Variant value = a->value_track_interpolate(i, time); if (value == Variant()) { @@ -1443,7 +1443,7 @@ void AnimationTree::_process_graph(double p_delta) { t->start = time; } } else if (t->playing) { - bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE; + bool loop = a->get_loop_mode() != Animation::LOOP_NONE; bool stop = false; @@ -1512,13 +1512,13 @@ void AnimationTree::_process_graph(double p_delta) { double at_anim_pos = 0.0; switch (anim->get_loop_mode()) { - case Animation::LoopMode::LOOP_NONE: { + case Animation::LOOP_NONE: { at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end } break; - case Animation::LoopMode::LOOP_LINEAR: { + case Animation::LOOP_LINEAR: { at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop } break; - case Animation::LoopMode::LOOP_PINGPONG: { + case Animation::LOOP_PINGPONG: { at_anim_pos = Math::pingpong(time - pos, (double)a->get_length()); } break; default: diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 3f09bf7f4b..37cd22568a 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -68,6 +68,7 @@ public: const Vector<real_t> *track_blends = nullptr; real_t blend = 0.0; bool seeked = false; + bool seek_root = false; int pingponged = 0; }; @@ -85,7 +86,7 @@ public: Vector<real_t> blends; State *state = nullptr; - double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, const Vector<StringName> &p_connections); + double _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, double p_time, bool p_seek, bool p_seek_root, const Vector<StringName> &p_connections); //all this is temporary StringName base_path; @@ -98,12 +99,12 @@ public: Array _get_filters() const; void _set_filters(const Array &p_filters); friend class AnimationNodeBlendTree; - double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr); + double _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr); protected: - void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0); - double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); - double blend_input(int p_input, double p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + void blend_animation(const StringName &p_animation, double p_time, double p_delta, bool p_seeked, bool p_seek_root, real_t p_blend, int p_pingponged = 0); + double blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); + double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true); void make_invalid(const String &p_reason); @@ -115,7 +116,7 @@ protected: GDVIRTUAL0RC(Array, _get_parameter_list) GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName) GDVIRTUAL1RC(Variant, _get_parameter_default_value, StringName) - GDVIRTUAL2RC(double, _process, double, bool) + GDVIRTUAL3RC(double, _process, double, bool, bool) GDVIRTUAL0RC(String, _get_caption) GDVIRTUAL0RC(bool, _has_filter) @@ -133,7 +134,7 @@ public: virtual void get_child_nodes(List<ChildNode> *r_child_nodes); - virtual double process(double p_time, bool p_seek); + virtual double process(double p_time, bool p_seek, bool p_seek_root); virtual String get_caption() const; int get_input_count() const; @@ -266,7 +267,7 @@ private: }; HashMap<NodePath, TrackCache *> track_cache; - Set<TrackCache *> playing_caches; + RBSet<TrackCache *> playing_caches; Ref<AnimationNode> root; diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 17b573b776..dfad91ecd5 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -68,7 +68,7 @@ bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) { } class SceneDebugger::RPCProfiler : public EngineProfiler { - Map<ObjectID, RPCNodeInfo> rpc_node_data; + HashMap<ObjectID, RPCNodeInfo> rpc_node_data; uint64_t last_profile_time = 0; void init_node(const ObjectID p_node) { @@ -345,22 +345,22 @@ void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) { return; } - Map<String, Set<Node *>> &edit_cache = debugger->live_scene_edit_cache; - Map<String, Set<Node *>>::Element *E = edit_cache.find(p_filename); + HashMap<String, RBSet<Node *>> &edit_cache = debugger->live_scene_edit_cache; + HashMap<String, RBSet<Node *>>::Iterator E = edit_cache.find(p_filename); if (E) { - E->get().erase(p_node); - if (E->get().size() == 0) { - edit_cache.erase(E); + E->value.erase(p_node); + if (E->value.size() == 0) { + edit_cache.remove(E); } } - Map<Node *, Map<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list; - Map<Node *, Map<ObjectID, Node *>>::Element *F = remove_list.find(p_node); + HashMap<Node *, HashMap<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list; + HashMap<Node *, HashMap<ObjectID, Node *>>::Iterator F = remove_list.find(p_node); if (F) { - for (const KeyValue<ObjectID, Node *> &G : F->get()) { + for (const KeyValue<ObjectID, Node *> &G : F->value) { memdelete(G.value); } - remove_list.erase(F); + remove_list.remove(F); } } @@ -408,47 +408,47 @@ SceneDebuggerObject::SceneDebuggerObject(ObjectID p_id) { } void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInstance *p_instance) { - typedef Map<const Script *, Set<StringName>> ScriptMemberMap; - typedef Map<const Script *, Map<StringName, Variant>> ScriptConstantsMap; + typedef HashMap<const Script *, RBSet<StringName>> ScriptMemberMap; + typedef HashMap<const Script *, HashMap<StringName, Variant>> ScriptConstantsMap; ScriptMemberMap members; if (p_instance) { - members[p_script] = Set<StringName>(); + members[p_script] = RBSet<StringName>(); p_script->get_members(&(members[p_script])); } ScriptConstantsMap constants; - constants[p_script] = Map<StringName, Variant>(); + constants[p_script] = HashMap<StringName, Variant>(); p_script->get_constants(&(constants[p_script])); Ref<Script> base = p_script->get_base_script(); while (base.is_valid()) { if (p_instance) { - members[base.ptr()] = Set<StringName>(); + members[base.ptr()] = RBSet<StringName>(); base->get_members(&(members[base.ptr()])); } - constants[base.ptr()] = Map<StringName, Variant>(); + constants[base.ptr()] = HashMap<StringName, Variant>(); base->get_constants(&(constants[base.ptr()])); base = base->get_base_script(); } // Members - for (ScriptMemberMap::Element *sm = members.front(); sm; sm = sm->next()) { - for (Set<StringName>::Element *E = sm->get().front(); E; E = E->next()) { + for (KeyValue<const Script *, RBSet<StringName>> sm : members) { + for (const StringName &E : sm.value) { Variant m; - if (p_instance->get(E->get(), m)) { - String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/"; - PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get()); + if (p_instance->get(E, m)) { + String script_path = sm.key == p_script ? "" : sm.key->get_path().get_file() + "/"; + PropertyInfo pi(m.get_type(), "Members/" + script_path + E); properties.push_back(SceneDebuggerProperty(pi, m)); } } } // Constants - for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) { - for (const KeyValue<StringName, Variant> &E : sc->get()) { - String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/"; + for (KeyValue<const Script *, HashMap<StringName, Variant>> &sc : constants) { + for (const KeyValue<StringName, Variant> &E : sc.value) { + String script_path = sc.key == p_script ? "" : sc.key->get_path().get_file() + "/"; if (E.value.get_type() == Variant::OBJECT) { Variant id = ((Object *)E.value)->get_instance_id(); PropertyInfo pi(id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object"); @@ -624,13 +624,13 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; @@ -668,13 +668,13 @@ void LiveEditor::_node_call_func(int p_id, const StringName &p_method, const Var base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; @@ -753,13 +753,13 @@ void LiveEditor::_create_node_func(const NodePath &p_parent, const String &p_typ base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; @@ -797,13 +797,13 @@ void LiveEditor::_instance_node_func(const NodePath &p_parent, const String &p_p base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; @@ -835,13 +835,13 @@ void LiveEditor::_remove_node_func(const NodePath &p_at) { base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F;) { - Set<Node *>::Element *N = F->next(); + for (RBSet<Node *>::Element *F = E->value.front(); F;) { + RBSet<Node *>::Element *N = F->next(); Node *n = F->get(); @@ -871,13 +871,13 @@ void LiveEditor::_remove_and_keep_node_func(const NodePath &p_at, ObjectID p_kee base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F;) { - Set<Node *>::Element *N = F->next(); + for (RBSet<Node *>::Element *F = E->value.front(); F;) { + RBSet<Node *>::Element *N = F->next(); Node *n = F->get(); @@ -910,13 +910,13 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F;) { - Set<Node *>::Element *N = F->next(); + for (RBSet<Node *>::Element *F = E->value.front(); F;) { + RBSet<Node *>::Element *N = F->next(); Node *n = F->get(); @@ -929,23 +929,23 @@ void LiveEditor::_restore_node_func(ObjectID p_id, const NodePath &p_at, int p_a } Node *n2 = n->get_node(p_at); - Map<Node *, Map<ObjectID, Node *>>::Element *EN = live_edit_remove_list.find(n); + HashMap<Node *, HashMap<ObjectID, Node *>>::Iterator EN = live_edit_remove_list.find(n); if (!EN) { continue; } - Map<ObjectID, Node *>::Element *FN = EN->get().find(p_id); + HashMap<ObjectID, Node *>::Iterator FN = EN->value.find(p_id); if (!FN) { continue; } - n2->add_child(FN->get()); + n2->add_child(FN->value); - EN->get().erase(FN); + EN->value.remove(FN); - if (EN->get().size() == 0) { - live_edit_remove_list.erase(EN); + if (EN->value.size() == 0) { + live_edit_remove_list.remove(EN); } F = N; @@ -963,13 +963,13 @@ void LiveEditor::_duplicate_node_func(const NodePath &p_at, const String &p_new_ base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; @@ -1002,13 +1002,13 @@ void LiveEditor::_reparent_node_func(const NodePath &p_at, const NodePath &p_new base = scene_tree->root->get_node(live_edit_root); } - Map<String, Set<Node *>>::Element *E = live_scene_edit_cache.find(live_edit_scene); + HashMap<String, RBSet<Node *>>::Iterator E = live_scene_edit_cache.find(live_edit_scene); if (!E) { return; //scene not editable } - for (Set<Node *>::Element *F = E->get().front(); F; F = F->next()) { - Node *n = F->get(); + for (Node *F : E->value) { + Node *n = F; if (base && !base->is_ancestor_of(n)) { continue; diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index 29d7da7d11..0daefa9609 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -132,14 +132,14 @@ public: class LiveEditor { private: friend class SceneDebugger; - Map<int, NodePath> live_edit_node_path_cache; - Map<int, String> live_edit_resource_cache; + HashMap<int, NodePath> live_edit_node_path_cache; + HashMap<int, String> live_edit_resource_cache; NodePath live_edit_root; String live_edit_scene; - Map<String, Set<Node *>> live_scene_edit_cache; - Map<Node *, Map<ObjectID, Node *>> live_edit_remove_list; + HashMap<String, RBSet<Node *>> live_scene_edit_cache; + HashMap<Node *, HashMap<ObjectID, Node *>> live_edit_remove_list; void _send_tree(); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 789c01adf3..776623f7ce 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -43,12 +43,12 @@ void BaseButton::_unpress_group() { status.pressed = true; } - for (Set<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) { - if (E->get() == this) { + for (BaseButton *E : button_group->buttons) { + if (E == this) { continue; } - E->get()->set_pressed(false); + E->set_pressed(false); } } @@ -485,24 +485,24 @@ BaseButton::~BaseButton() { } void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) { - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { - r_buttons->push_back(E->get()); + for (BaseButton *E : buttons) { + r_buttons->push_back(E); } } Array ButtonGroup::_get_buttons() { Array btns; - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { - btns.push_back(E->get()); + for (const BaseButton *E : buttons) { + btns.push_back(E); } return btns; } BaseButton *ButtonGroup::get_pressed_button() { - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { - if (E->get()->is_pressed()) { - return E->get(); + for (BaseButton *E : buttons) { + if (E->is_pressed()) { + return E; } } diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index f4f9b88868..0b70d285ee 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -143,7 +143,7 @@ VARIANT_ENUM_CAST(BaseButton::ActionMode) class ButtonGroup : public Resource { GDCLASS(ButtonGroup, Resource); friend class BaseButton; - Set<BaseButton *> buttons; + RBSet<BaseButton *> buttons; protected: static void _bind_methods(); diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 251648da69..df695feba8 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -52,7 +52,7 @@ void BoxContainer::_resort() { int stretch_min = 0; int stretch_avail = 0; float stretch_ratio_total = 0.0; - Map<Control *, _MinSizeCache> min_size_cache; + HashMap<Control *, _MinSizeCache> min_size_cache; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index ff194f979d..c54897035a 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -60,11 +60,11 @@ Size2 Button::get_minimum_size() const { } } } - - Ref<Font> font = get_theme_font(SNAME("font")); - float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); - - minsize.height = MAX(font_height, minsize.height); + if (!xl_text.is_empty()) { + Ref<Font> font = get_theme_font(SNAME("font")); + float font_height = font->get_height(get_theme_font_size(SNAME("font_size"))); + minsize.height = MAX(font_height, minsize.height); + } return get_theme_stylebox(SNAME("normal"))->get_minimum_size() + minsize; } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index b2aa4030b7..22e9763929 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -740,8 +740,8 @@ void CodeEdit::set_auto_indent_prefixes(const TypedArray<String> &p_prefixes) { TypedArray<String> CodeEdit::get_auto_indent_prefixes() const { TypedArray<String> prefixes; - for (const Set<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) { - prefixes.push_back(String::chr(E->get())); + for (const char32_t &E : auto_indent_prefixes) { + prefixes.push_back(String::chr(E)); } return prefixes; } @@ -1628,7 +1628,7 @@ Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const { start_position.y = -1; start_position.x = -1; - bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value()) != -1; + bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->get()) != -1; /* Check the keys for this line. */ for (const KeyValue<int, int> &E : delimiter_cache[p_line]) { @@ -1752,8 +1752,8 @@ void CodeEdit::set_code_completion_prefixes(const TypedArray<String> &p_prefixes TypedArray<String> CodeEdit::get_code_completion_prefixes() const { TypedArray<String> prefixes; - for (const Set<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) { - prefixes.push_back(String::chr(E->get())); + for (const char32_t &E : code_completion_prefixes) { + prefixes.push_back(String::chr(E)); } return prefixes; } @@ -2402,7 +2402,7 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) { } } else { for (int i = start_line; i < end_line; i++) { - delimiter_cache.insert(i, Map<int, int>()); + delimiter_cache.insert(i, RBMap<int, int>()); } } } @@ -2539,7 +2539,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value(); bool in_region = region != -1 && delimiters[region].type == p_type; - for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) { + for (RBMap<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) { /* If column is specified, loop until the key is larger then the column. */ if (p_column != -1) { if (E->key() > p_column) { diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 135dd32780..0b00735f46 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -58,7 +58,7 @@ private: String indent_text = "\t"; bool auto_indent = false; - Set<char32_t> auto_indent_prefixes; + RBSet<char32_t> auto_indent_prefixes; bool indent_using_spaces = false; int _calculate_spaces_till_next_left_indent(int p_column) const; @@ -176,7 +176,7 @@ private: * ] * ] */ - Vector<Map<int, int>> delimiter_cache; + Vector<RBMap<int, int>> delimiter_cache; void _update_delimiter_cache(int p_from_line = 0, int p_to_line = -1); int _is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const; @@ -214,7 +214,7 @@ private: int code_completion_longest_line = 0; Rect2i code_completion_rect; - Set<char32_t> code_completion_prefixes; + RBSet<char32_t> code_completion_prefixes; List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted; List<ScriptLanguage::CodeCompletionOption> code_completion_option_sources; String code_completion_base; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 54fa726260..5c5b2683a4 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -471,6 +471,13 @@ void Control::_validate_property(PropertyInfo &property) const { property.hint_string = hint_string; } + if (property.name == "mouse_force_pass_scroll_events") { + // Disable force pass if the control is not stopping the event. + if (data.mouse_filter != MOUSE_FILTER_STOP) { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } + // Validate which positioning properties should be displayed depending on the parent and the layout mode. Node *parent_node = get_parent_control(); if (!parent_node) { @@ -712,35 +719,25 @@ void Control::_notification(int p_notification) { data.parent_window = Object::cast_to<Window>(get_parent()); data.is_rtl_dirty = true; - Node *parent = this; //meh + CanvasItem *node = this; Control *parent_control = nullptr; - bool subwindow = false; - - while (parent) { - parent = parent->get_parent(); + while (!node->is_set_as_top_level()) { + CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent()); if (!parent) { break; } - CanvasItem *ci = Object::cast_to<CanvasItem>(parent); - if (ci && ci->is_set_as_top_level()) { - subwindow = true; - break; - } - parent_control = Object::cast_to<Control>(parent); - if (parent_control) { break; - } else if (ci) { - } else { - break; } + + node = parent; } - if (parent_control && !subwindow) { - //do nothing, has a parent control and not top_level + if (parent_control) { + // Do nothing, has a parent control. if (data.theme.is_null() && parent_control->data.theme_owner) { data.theme_owner = parent_control->data.theme_owner; notification(NOTIFICATION_THEME_CHANGED); @@ -2931,6 +2928,7 @@ int Control::get_v_size_flags() const { void Control::set_mouse_filter(MouseFilter p_filter) { ERR_FAIL_INDEX(p_filter, 3); data.mouse_filter = p_filter; + notify_property_list_changed(); update_configuration_warnings(); } @@ -2938,6 +2936,14 @@ Control::MouseFilter Control::get_mouse_filter() const { return data.mouse_filter; } +void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) { + data.force_pass_scroll_events = p_force_pass_scroll_events; +} + +bool Control::is_force_pass_scroll_events() const { + return data.force_pass_scroll_events; +} + void Control::warp_mouse(const Point2 &p_position) { ERR_FAIL_COND(!is_inside_tree()); get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position)); @@ -3250,6 +3256,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mouse_filter", "filter"), &Control::set_mouse_filter); ClassDB::bind_method(D_METHOD("get_mouse_filter"), &Control::get_mouse_filter); + ClassDB::bind_method(D_METHOD("set_force_pass_scroll_events", "force_pass_scroll_events"), &Control::set_force_pass_scroll_events); + ClassDB::bind_method(D_METHOD("is_force_pass_scroll_events"), &Control::is_force_pass_scroll_events); + ClassDB::bind_method(D_METHOD("set_clip_contents", "enable"), &Control::set_clip_contents); ClassDB::bind_method(D_METHOD("is_clipping_contents"), &Control::is_clipping_contents); @@ -3331,6 +3340,7 @@ void Control::_bind_methods() { ADD_GROUP("Mouse", "mouse_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_force_pass_scroll_events"), "set_force_pass_scroll_events", "is_force_pass_scroll_events"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); ADD_GROUP("Theme", "theme_"); diff --git a/scene/gui/control.h b/scene/gui/control.h index 65b71d74f8..f18dd99bff 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -190,6 +190,7 @@ private: Point2 custom_minimum_size; MouseFilter mouse_filter = MOUSE_FILTER_STOP; + bool force_pass_scroll_events = true; bool clip_contents = false; @@ -216,12 +217,12 @@ private: NodePath focus_prev; bool bulk_theme_override = false; - HashMap<StringName, Ref<Texture2D>> icon_override; - HashMap<StringName, Ref<StyleBox>> style_override; - HashMap<StringName, Ref<Font>> font_override; - HashMap<StringName, int> font_size_override; - HashMap<StringName, Color> color_override; - HashMap<StringName, int> constant_override; + Theme::ThemeIconMap icon_override; + Theme::ThemeStyleMap style_override; + Theme::ThemeFontMap font_override; + Theme::ThemeFontSizeMap font_size_override; + Theme::ThemeColorMap color_override; + Theme::ThemeConstantMap constant_override; } data; @@ -468,6 +469,9 @@ public: void set_mouse_filter(MouseFilter p_filter); MouseFilter get_mouse_filter() const; + void set_force_pass_scroll_events(bool p_force_pass_scroll_events); + bool is_force_pass_scroll_events() const; + /* SKINNING */ void begin_bulk_theme_override(); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 6da5340ca4..1725816c31 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -915,7 +915,7 @@ void FileDialog::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title"); ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open File,Open Files,Open Folder,Open Any,Save"), "set_file_mode", "get_file_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User Data,File System"), "set_access", "get_access"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir"); diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 1e5863b845..30b694da76 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -49,7 +49,7 @@ void FlowContainer::_resort() { bool rtl = is_layout_rtl(); - Map<Control *, Size2i> children_minsize_cache; + HashMap<Control *, Size2i> children_minsize_cache; Vector<_LineData> lines_data; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 4c8006e33d..ccf7e2828a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1343,7 +1343,19 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { emit_signal(SNAME("paste_nodes_request")); accept_event(); } else if (p_ev->is_action("ui_graph_delete")) { - emit_signal(SNAME("delete_nodes_request")); + TypedArray<StringName> nodes; + + for (int i = 0; i < get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); + if (!gn) { + continue; + } + if (gn->is_selected() && gn->is_close_button_visible()) { + nodes.push_back(gn->get_name()); + } + } + + emit_signal(SNAME("delete_nodes_request"), nodes); accept_event(); } } @@ -1684,11 +1696,11 @@ void GraphEdit::set_warped_panning(bool p_warped) { warped_panning = p_warped; } -int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, const Set<StringName> &r_v) { +int GraphEdit::_set_operations(SET_OPERATIONS p_operation, RBSet<StringName> &r_u, const RBSet<StringName> &r_v) { switch (p_operation) { case GraphEdit::IS_EQUAL: { - for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) { - if (!r_v.has(E->get())) { + for (const StringName &E : r_u) { + if (!r_v.has(E)) { return 0; } } @@ -1698,15 +1710,15 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, if (r_u.size() == r_v.size() && !r_u.size()) { return 1; } - for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) { - if (!r_v.has(E->get())) { + for (const StringName &E : r_u) { + if (!r_v.has(E)) { return 0; } } return 1; } break; case GraphEdit::DIFFERENCE: { - for (Set<StringName>::Element *E = r_u.front(); E; E = E->next()) { + for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) { if (r_v.has(E->get())) { r_u.erase(E->get()); } @@ -1714,9 +1726,9 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, return r_u.size(); } break; case GraphEdit::UNION: { - for (Set<StringName>::Element *E = r_v.front(); E; E = E->next()) { - if (!r_u.has(E->get())) { - r_u.insert(E->get()); + for (const StringName &E : r_v) { + if (!r_u.has(E)) { + r_u.insert(E); } } return r_u.size(); @@ -1727,28 +1739,28 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, return -1; } -HashMap<int, Vector<StringName>> GraphEdit::_layering(const Set<StringName> &r_selected_nodes, const HashMap<StringName, Set<StringName>> &r_upper_neighbours) { +HashMap<int, Vector<StringName>> GraphEdit::_layering(const RBSet<StringName> &r_selected_nodes, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours) { HashMap<int, Vector<StringName>> l; - Set<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z; + RBSet<StringName> p = r_selected_nodes, q = r_selected_nodes, u, z; int current_layer = 0; bool selected = false; while (!_set_operations(GraphEdit::IS_EQUAL, q, u)) { _set_operations(GraphEdit::DIFFERENCE, p, u); - for (const Set<StringName>::Element *E = p.front(); E; E = E->next()) { - Set<StringName> n = r_upper_neighbours[E->get()]; + for (const StringName &E : p) { + RBSet<StringName> n = r_upper_neighbours[E]; if (_set_operations(GraphEdit::IS_SUBSET, n, z)) { Vector<StringName> t; - t.push_back(E->get()); + t.push_back(E); if (!l.has(current_layer)) { l.insert(current_layer, Vector<StringName>{}); } selected = true; t.append_array(l[current_layer]); l.insert(current_layer, t); - Set<StringName> V; - V.insert(E->get()); + RBSet<StringName> V; + V.insert(E); _set_operations(GraphEdit::UNION, u, V); } } @@ -1789,10 +1801,10 @@ Vector<StringName> GraphEdit::_split(const Vector<StringName> &r_layer, const Ha return left; } -void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours, const Set<StringName> &r_selected_nodes) { - for (const Set<StringName>::Element *E = r_selected_nodes.front(); E; E = E->next()) { - r_root[E->get()] = E->get(); - r_align[E->get()] = E->get(); +void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours, const RBSet<StringName> &r_selected_nodes) { + for (const StringName &E : r_selected_nodes) { + r_root[E] = E; + r_align[E] = E; } if (r_layers.size() == 1) { @@ -1829,7 +1841,7 @@ void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, c } } -void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours) { +void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours) { if (r_layers.size() == 1) { return; } @@ -1867,10 +1879,10 @@ void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layer } } -void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const Set<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) { - for (const Set<StringName>::Element *E = r_block_heads.front(); E; E = E->next()) { +void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const RBSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info) { + for (const StringName &E : r_block_heads) { real_t left = 0; - StringName u = E->get(); + StringName u = E; StringName v = r_align[u]; while (u != v && (StringName)r_root[u] != v) { String _connection = String(u) + " " + String(v); @@ -1891,11 +1903,11 @@ void GraphEdit::_calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictio v = (StringName)r_align[v]; } - u = E->get(); + u = E; do { r_inner_shifts[u] = (real_t)r_inner_shifts[u] - left; u = (StringName)r_align[u]; - } while (u != E->get()); + } while (u != E); } } @@ -2040,7 +2052,7 @@ void GraphEdit::arrange_nodes() { } Dictionary node_names; - Set<StringName> selected_nodes; + RBSet<StringName> selected_nodes; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -2051,7 +2063,7 @@ void GraphEdit::arrange_nodes() { node_names[gn->get_name()] = gn; } - HashMap<StringName, Set<StringName>> upper_neighbours; + HashMap<StringName, RBSet<StringName>> upper_neighbours; HashMap<StringName, Pair<int, int>> port_info; Vector2 origin(FLT_MAX, FLT_MAX); @@ -2066,7 +2078,7 @@ void GraphEdit::arrange_nodes() { if (gn->is_selected()) { selected_nodes.insert(gn->get_name()); - Set<StringName> s; + RBSet<StringName> s; for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from]); if (E->get().to == gn->get_name() && p_from->is_selected()) { @@ -2103,35 +2115,35 @@ void GraphEdit::arrange_nodes() { HashMap<StringName, Vector2> new_positions; Vector2 default_position(FLT_MAX, FLT_MAX); Dictionary inner_shift; - Set<StringName> block_heads; + RBSet<StringName> block_heads; - for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) { - inner_shift[E->get()] = 0.0f; - sink[E->get()] = E->get(); - shift[E->get()] = FLT_MAX; - new_positions.insert(E->get(), default_position); - if ((StringName)root[E->get()] == E->get()) { - block_heads.insert(E->get()); + for (const StringName &E : selected_nodes) { + inner_shift[E] = 0.0f; + sink[E] = E; + shift[E] = FLT_MAX; + new_positions.insert(E, default_position); + if ((StringName)root[E] == E) { + block_heads.insert(E); } } _calculate_inner_shifts(inner_shift, root, node_names, align, block_heads, port_info); - for (const Set<StringName>::Element *E = block_heads.front(); E; E = E->next()) { - _place_block(E->get(), gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions); + for (const StringName &E : block_heads) { + _place_block(E, gap_v, layers, root, align, node_names, inner_shift, sink, shift, new_positions); } origin.y = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().y - (new_positions[layers[0][0]].y + (float)inner_shift[layers[0][0]]); origin.x = Object::cast_to<GraphNode>(node_names[layers[0][0]])->get_position_offset().x; - for (const Set<StringName>::Element *E = block_heads.front(); E; E = E->next()) { - StringName u = E->get(); - float start_from = origin.y + new_positions[E->get()].y; + for (const StringName &E : block_heads) { + StringName u = E; + float start_from = origin.y + new_positions[E].y; do { Vector2 cal_pos; cal_pos.y = start_from + (real_t)inner_shift[u]; new_positions.insert(u, cal_pos); u = align[u]; - } while (u != E->get()); + } while (u != E); } // Compute horizontal coordinates individually for layers to get uniform gap. @@ -2169,10 +2181,10 @@ void GraphEdit::arrange_nodes() { } emit_signal(SNAME("begin_node_move")); - for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) { - GraphNode *gn = Object::cast_to<GraphNode>(node_names[E->get()]); + for (const StringName &E : selected_nodes) { + GraphNode *gn = Object::cast_to<GraphNode>(node_names[E]); gn->set_drag(true); - Vector2 pos = (new_positions[E->get()]); + Vector2 pos = (new_positions[E]); if (is_using_snap()) { const int snap = get_snap(); @@ -2290,7 +2302,7 @@ void GraphEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); - ADD_SIGNAL(MethodInfo("delete_nodes_request")); + ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName"))); ADD_SIGNAL(MethodInfo("begin_node_move")); ADD_SIGNAL(MethodInfo("end_node_move")); ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset"))); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index f556fcdd23..9e34d5528f 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -228,9 +228,9 @@ private: } }; - Set<ConnType> valid_connection_types; - Set<int> valid_left_disconnect_types; - Set<int> valid_right_disconnect_types; + RBSet<ConnType> valid_connection_types; + RBSet<int> valid_left_disconnect_types; + RBSet<int> valid_right_disconnect_types; HashMap<StringName, Vector<GraphNode *>> comment_enclosed_nodes; void _update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes); @@ -258,12 +258,12 @@ private: UNION, }; - int _set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, const Set<StringName> &r_v); - HashMap<int, Vector<StringName>> _layering(const Set<StringName> &r_selected_nodes, const HashMap<StringName, Set<StringName>> &r_upper_neighbours); + int _set_operations(SET_OPERATIONS p_operation, RBSet<StringName> &r_u, const RBSet<StringName> &r_v); + HashMap<int, Vector<StringName>> _layering(const RBSet<StringName> &r_selected_nodes, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours); Vector<StringName> _split(const Vector<StringName> &r_layer, const HashMap<StringName, Dictionary> &r_crossings); - void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours, const Set<StringName> &r_selected_nodes); - void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, Set<StringName>> &r_upper_neighbours); - void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const Set<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info); + void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours, const RBSet<StringName> &r_selected_nodes); + void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, RBSet<StringName>> &r_upper_neighbours); + void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const RBSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info); float _calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions); void _place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index e3ecd17ed8..45f036d8fc 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -93,11 +93,13 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { si.color_right = p_value; } else if (what == "right_icon") { si.custom_slot_right = p_value; + } else if (what == "draw_stylebox") { + si.draw_stylebox = p_value; } else { return false; } - set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right); + set_slot(idx, si.enable_left, si.type_left, si.color_left, si.enable_right, si.type_right, si.color_right, si.custom_slot_left, si.custom_slot_right, si.draw_stylebox); update(); return true; } @@ -144,6 +146,8 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { r_ret = si.color_right; } else if (what == "right_icon") { r_ret = si.custom_slot_right; + } else if (what == "draw_stylebox") { + r_ret = si.draw_stylebox; } else { return false; } @@ -175,7 +179,7 @@ void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, base + "right_type")); p_list->push_back(PropertyInfo(Variant::COLOR, base + "right_color")); p_list->push_back(PropertyInfo(Variant::OBJECT, base + "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); - + p_list->push_back(PropertyInfo(Variant::BOOL, base + "draw_stylebox")); idx++; } } @@ -185,6 +189,7 @@ void GraphNode::_resort() { Size2i new_size = get_size(); Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame")); + Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot")); int sep = get_theme_constant(SNAME("separation")); @@ -193,7 +198,7 @@ void GraphNode::_resort() { int stretch_min = 0; int stretch_avail = 0; float stretch_ratio_total = 0; - Map<Control *, _MinSizeCache> min_size_cache; + HashMap<Control *, _MinSizeCache> min_size_cache; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -204,7 +209,7 @@ void GraphNode::_resort() { continue; } - Size2i size = c->get_combined_minimum_size(); + Size2i size = c->get_combined_minimum_size() + (slot_info[i].draw_stylebox ? sb_slot->get_minimum_size() : Size2()); _MinSizeCache msc; stretch_min += size.height; @@ -312,7 +317,9 @@ void GraphNode::_resort() { int size = to - from; - Rect2 rect(sb->get_margin(SIDE_LEFT), from, w, size); + float margin = sb->get_margin(SIDE_LEFT) + (slot_info[i].draw_stylebox ? sb_slot->get_margin(SIDE_LEFT) : 0); + float width = w - (slot_info[i].draw_stylebox ? sb_slot->get_minimum_size().x : 0); + Rect2 rect(margin, from, width, size); fit_child_in_rect(c, rect); cache_y.push_back(from - sb->get_margin(SIDE_TOP) + size * 0.5); @@ -351,14 +358,14 @@ void GraphNode::_notification(int p_what) { Ref<StyleBox> sb; if (comment) { - sb = get_theme_stylebox(selected ? "comment_focus" : "comment"); + sb = get_theme_stylebox(selected ? SNAME("comment_focus") : SNAME("comment")); } else { - sb = get_theme_stylebox(selected ? "selected_frame" : "frame"); + sb = get_theme_stylebox(selected ? SNAME("selected_frame") : SNAME("frame")); } - //sb=sb->duplicate(); - //sb->call("set_modulate",modulate); + Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot")); + Ref<Texture2D> port = get_theme_icon(SNAME("port")); Ref<Texture2D> close = get_theme_icon(SNAME("close")); Ref<Texture2D> resizer = get_theme_icon(SNAME("resizer")); @@ -389,13 +396,9 @@ void GraphNode::_notification(int p_what) { int w = get_size().width - sb->get_minimum_size().x; - if (show_close) { - w -= close->get_width(); - } - title_buf->draw(get_canvas_item(), Point2(sb->get_margin(SIDE_LEFT) + title_h_offset, -title_buf->get_size().y + title_offset), title_color); if (show_close) { - Vector2 cpos = Point2(w + sb->get_margin(SIDE_LEFT) + close_h_offset, -close->get_height() + close_offset); + Vector2 cpos = Point2(w + sb->get_margin(SIDE_LEFT) + close_h_offset - close->get_width(), -close->get_height() + close_offset); draw_texture(close, cpos, close_color); close_rect.position = cpos; close_rect.size = close->get_size(); @@ -411,7 +414,7 @@ void GraphNode::_notification(int p_what) { continue; } const Slot &s = slot_info[E.key]; - //left + // Left port. if (s.enable_left) { Ref<Texture2D> p = port; if (s.custom_slot_left.is_valid()) { @@ -419,6 +422,7 @@ void GraphNode::_notification(int p_what) { } p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E.key]), s.color_left); } + // Right port. if (s.enable_right) { Ref<Texture2D> p = port; if (s.custom_slot_right.is_valid()) { @@ -426,6 +430,15 @@ void GraphNode::_notification(int p_what) { } p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E.key]), s.color_right); } + + // Draw slot stylebox. + if (s.draw_stylebox) { + Control *c = Object::cast_to<Control>(get_child(E.key)); + Rect2 c_rect = c->get_rect(); + c_rect.position.x = sb->get_margin(SIDE_LEFT); + c_rect.size.width = w; + draw_style_box(sb_slot, c_rect); + } } if (resizable) { @@ -482,7 +495,7 @@ void GraphNode::_validate_property(PropertyInfo &property) const { } #endif -void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) { +void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right, bool p_draw_stylebox) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx)); if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) && @@ -501,6 +514,7 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C s.color_right = p_color_right; s.custom_slot_left = p_custom_left; s.custom_slot_right = p_custom_right; + s.draw_stylebox = p_draw_stylebox; slot_info[p_idx] = s; update(); connpos_dirty = true; @@ -622,16 +636,39 @@ Color GraphNode::get_slot_color_right(int p_idx) const { return slot_info[p_idx].color_right; } +bool GraphNode::is_slot_draw_stylebox(int p_idx) const { + if (!slot_info.has(p_idx)) { + return false; + } + return slot_info[p_idx].draw_stylebox; +} + +void GraphNode::set_slot_draw_stylebox(int p_idx, bool p_enable) { + ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set draw_stylebox for the slot with p_idx (%d) lesser than zero.", p_idx)); + + slot_info[p_idx].draw_stylebox = p_enable; + update(); + connpos_dirty = true; + + emit_signal(SNAME("slot_updated"), p_idx); +} + Size2 GraphNode::get_minimum_size() const { - int sep = get_theme_constant(SNAME("separation")); Ref<StyleBox> sb = get_theme_stylebox(SNAME("frame")); + Ref<StyleBox> sb_slot = get_theme_stylebox(SNAME("slot")); + + int sep = get_theme_constant(SNAME("separation")); + int title_h_offset = get_theme_constant(SNAME("title_h_offset")); + bool first = true; Size2 minsize; - minsize.x = title_buf->get_size().x; + minsize.x = title_buf->get_size().x + title_h_offset; if (show_close) { + int close_h_offset = get_theme_constant(SNAME("close_h_offset")); Ref<Texture2D> close = get_theme_icon(SNAME("close")); - minsize.x += sep + close->get_width(); + //TODO: Remove this magic number after GraphNode rework. + minsize.x += 12 + close->get_width() + close_h_offset; } for (int i = 0; i < get_child_count(); i++) { @@ -644,6 +681,9 @@ Size2 GraphNode::get_minimum_size() const { } Size2i size = c->get_combined_minimum_size(); + if (slot_info.has(i)) { + size += slot_info[i].draw_stylebox ? sb_slot->get_minimum_size() : Size2(); + } minsize.y += size.y; minsize.x = MAX(minsize.x, size.x); @@ -989,7 +1029,7 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language); ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language); - ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>())); + ClassDB::bind_method(D_METHOD("set_slot", "idx", "enable_left", "type_left", "color_left", "enable_right", "type_right", "color_right", "custom_left", "custom_right", "enable"), &GraphNode::set_slot, DEFVAL(Ref<Texture2D>()), DEFVAL(Ref<Texture2D>()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("clear_slot", "idx"), &GraphNode::clear_slot); ClassDB::bind_method(D_METHOD("clear_all_slots"), &GraphNode::clear_all_slots); @@ -1011,6 +1051,9 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slot_color_right", "idx", "color_right"), &GraphNode::set_slot_color_right); ClassDB::bind_method(D_METHOD("get_slot_color_right", "idx"), &GraphNode::get_slot_color_right); + ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "idx"), &GraphNode::is_slot_draw_stylebox); + ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "idx", "draw_stylebox"), &GraphNode::set_slot_draw_stylebox); + ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphNode::set_position_offset); ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphNode::get_position_offset); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 7eb5f27cff..9481a7452d 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -54,6 +54,7 @@ private: Color color_right = Color(1, 1, 1, 1); Ref<Texture2D> custom_slot_left; Ref<Texture2D> custom_slot_right; + bool draw_stylebox = true; }; String title; @@ -85,7 +86,7 @@ private: Vector<ConnCache> conn_input_cache; Vector<ConnCache> conn_output_cache; - Map<int, Slot> slot_info; + HashMap<int, Slot> slot_info; bool connpos_dirty = true; @@ -115,7 +116,7 @@ protected: public: bool has_point(const Point2 &p_point) const override; - void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>()); + void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>(), bool p_draw_stylebox = true); void clear_slot(int p_idx); void clear_all_slots(); @@ -137,6 +138,9 @@ public: void set_slot_color_right(int p_idx, const Color &p_color_right); Color get_slot_color_right(int p_idx) const; + bool is_slot_draw_stylebox(int p_idx) const; + void set_slot_draw_stylebox(int p_idx, bool p_enable); + void set_title(const String &p_title); String get_title() const; @@ -185,7 +189,9 @@ public: virtual Vector<int> get_allowed_size_flags_horizontal() const override; virtual Vector<int> get_allowed_size_flags_vertical() const override; - bool is_resizing() const { return resizing; } + bool is_resizing() const { + return resizing; + } GraphNode(); }; diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index b58bb4d74a..5d9484806b 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -33,10 +33,10 @@ void GridContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { - Map<int, int> col_minw; // Max of min_width of all controls in each col (indexed by col). - Map<int, int> row_minh; // Max of min_height of all controls in each row (indexed by row). - Set<int> col_expanded; // Columns which have the SIZE_EXPAND flag set. - Set<int> row_expanded; // Rows which have the SIZE_EXPAND flag set. + HashMap<int, int> col_minw; // Max of min_width of all controls in each col (indexed by col). + HashMap<int, int> row_minh; // Max of min_height of all controls in each row (indexed by row). + RBSet<int> col_expanded; // Columns which have the SIZE_EXPAND flag set. + RBSet<int> row_expanded; // Rows which have the SIZE_EXPAND flag set. int hsep = get_theme_constant(SNAME("h_separation")); int vsep = get_theme_constant(SNAME("v_separation")); @@ -104,11 +104,11 @@ void GridContainer::_notification(int p_what) { // Check if all minwidth constraints are OK if we use the remaining space. can_fit = true; int max_index = col_expanded.front()->get(); - for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) { - if (col_minw[E->get()] > col_minw[max_index]) { - max_index = E->get(); + for (const int &E : col_expanded) { + if (col_minw[E] > col_minw[max_index]) { + max_index = E; } - if (can_fit && (remaining_space.width / col_expanded.size()) < col_minw[E->get()]) { + if (can_fit && (remaining_space.width / col_expanded.size()) < col_minw[E]) { can_fit = false; } } @@ -125,11 +125,11 @@ void GridContainer::_notification(int p_what) { // Check if all minheight constraints are OK if we use the remaining space. can_fit = true; int max_index = row_expanded.front()->get(); - for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) { - if (row_minh[E->get()] > row_minh[max_index]) { - max_index = E->get(); + for (const int &E : row_expanded) { + if (row_minh[E] > row_minh[max_index]) { + max_index = E; } - if (can_fit && (remaining_space.height / row_expanded.size()) < row_minh[E->get()]) { + if (can_fit && (remaining_space.height / row_expanded.size()) < row_minh[E]) { can_fit = false; } } @@ -142,13 +142,47 @@ void GridContainer::_notification(int p_what) { } // Finally, fit the nodes. - int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0; - int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0; + int col_remaining_pixel = 0; + int col_expand = 0; + if (col_expanded.size() > 0) { + col_expand = remaining_space.width / col_expanded.size(); + col_remaining_pixel = remaining_space.width - col_expanded.size() * col_expand; + } + + int row_remaining_pixel = 0; + int row_expand = 0; + if (row_expanded.size() > 0) { + row_expand = remaining_space.height / row_expanded.size(); + row_remaining_pixel = remaining_space.height - row_expanded.size() * row_expand; + } + bool rtl = is_layout_rtl(); int col_ofs = 0; int row_ofs = 0; + // Calculate the index of rows and columns that receive the remaining pixel. + int col_remaining_pixel_index = 0; + for (int i = 0; i < max_col; i++) { + if (col_remaining_pixel == 0) { + break; + } + if (col_expanded.has(i)) { + col_remaining_pixel_index = i + 1; + col_remaining_pixel--; + } + } + int row_remaining_pixel_index = 0; + for (int i = 0; i < max_row; i++) { + if (row_remaining_pixel == 0) { + break; + } + if (row_expanded.has(i)) { + row_remaining_pixel_index = i + 1; + row_remaining_pixel--; + } + } + valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -167,17 +201,30 @@ void GridContainer::_notification(int p_what) { } if (row > 0) { row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep; + + if (row_expanded.has(row - 1) && row - 1 < row_remaining_pixel_index) { + // Apply the remaining pixel of the previous row. + row_ofs++; + } } } + Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); + + // Add the remaining pixel to the expanding columns and rows, starting from left and top. + if (col_expanded.has(col) && col < col_remaining_pixel_index) { + s.x++; + } + if (row_expanded.has(row) && row < row_remaining_pixel_index) { + s.y++; + } + if (rtl) { - Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); Point2 p(col_ofs - s.width, row_ofs); fit_child_in_rect(c, Rect2(p, s)); col_ofs -= s.width + hsep; } else { Point2 p(col_ofs, row_ofs); - Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); fit_child_in_rect(c, Rect2(p, s)); col_ofs += s.width + hsep; } @@ -214,8 +261,8 @@ void GridContainer::_bind_methods() { } Size2 GridContainer::get_minimum_size() const { - Map<int, int> col_minw; - Map<int, int> row_minh; + HashMap<int, int> col_minw; + HashMap<int, int> row_minh; int hsep = get_theme_constant(SNAME("h_separation")); int vsep = get_theme_constant(SNAME("v_separation")); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 1feee017c2..316fee53fe 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -187,7 +187,7 @@ void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const { pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); p_list->push_back(pi); - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"); + pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button"); pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); p_list->push_back(pi); diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index b9e3e7814e..6532fc5934 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -108,11 +108,25 @@ void Popup::_close_pressed() { _deinitialize_visible_parents(); - call_deferred(SNAME("hide")); + // Hide after returning to process events, but only if we don't + // get popped up in the interim. + call_deferred(SNAME("_popup_conditional_hide")); +} + +void Popup::_post_popup() { + Window::_post_popup(); + popped_up = true; +} + +void Popup::_popup_conditional_hide() { + if (!popped_up) { + hide(); + } } void Popup::_bind_methods() { ADD_SIGNAL(MethodInfo("popup_hide")); + ClassDB::bind_method(D_METHOD("_popup_conditional_hide"), &Popup::_popup_conditional_hide); } Rect2i Popup::_popup_adjust_rect() const { diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 6211af4d20..27f46d4a97 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -57,6 +57,10 @@ protected: void _notification(int p_what); static void _bind_methods(); + void _popup_conditional_hide(); + + virtual void _post_popup() override; + public: Popup(); ~Popup(); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 98d76875cb..12587b7e73 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -117,7 +117,7 @@ class PopupMenu : public Popup { bool hide_on_multistate_item_selection = false; Vector2 moved; - Map<Ref<Shortcut>, int> shortcut_refcount; + HashMap<Ref<Shortcut>, int> shortcut_refcount; void _ref_shortcut(Ref<Shortcut> p_sc); void _unref_shortcut(Ref<Shortcut> p_sc); diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 8e66826e9d..fae6688452 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -50,8 +50,8 @@ void Range::_value_changed_notify() { } void Range::Shared::emit_value_changed() { - for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) { - Range *r = E->get(); + for (Range *E : owners) { + Range *r = E; if (!r->is_inside_tree()) { continue; } @@ -70,8 +70,8 @@ void Range::_validate_values() { } void Range::Shared::emit_changed(const char *p_what) { - for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) { - Range *r = E->get(); + for (Range *E : owners) { + Range *r = E; if (!r->is_inside_tree()) { continue; } diff --git a/scene/gui/range.h b/scene/gui/range.h index 46b0d39202..a59bfa9677 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -45,7 +45,7 @@ class Range : public Control { bool exp_ratio = false; bool allow_greater = false; bool allow_lesser = false; - Set<Range *> owners; + RBSet<Range *> owners; void emit_value_changed(); void emit_changed(const char *p_what = ""); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 7ed28ac3c8..78fd682c35 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -209,9 +209,10 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const { void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) { ERR_FAIL_COND(p_frame == nullptr); - ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)p_frame->lines.size()); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; + MutexLock lock(l.text_buf->get_mutex()); RID t = l.text_buf->get_rid(); int spans = TS->shaped_get_span_count(t); @@ -231,7 +232,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< } } - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { switch (it->type) { case ITEM_TABLE: { @@ -239,7 +240,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< for (Item *E : table->subitems) { ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. ItemFrame *frame = static_cast<ItemFrame *>(E); - for (int i = 0; i < frame->lines.size(); i++) { + for (int i = 0; i < (int)frame->lines.size(); i++) { _update_line_font(frame, i, p_base_font, p_base_font_size); } } @@ -250,11 +251,12 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< } } -void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) { - ERR_FAIL_COND(p_frame == nullptr); - ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); +float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h) { + ERR_FAIL_COND_V(p_frame == nullptr, p_h); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), p_h); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; + MutexLock lock(l.text_buf->get_mutex()); l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size); l.text_buf->set_width(p_width - l.offset.x); @@ -265,7 +267,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.text_buf->tab_align(tabs); } - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { switch (it->type) { case ITEM_TABLE: { @@ -275,16 +277,18 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> int col_count = table->columns.size(); for (int i = 0; i < col_count; i++) { - table->columns.write[i].width = 0; + table->columns[i].width = 0; } int idx = 0; for (Item *E : table->subitems) { ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. ItemFrame *frame = static_cast<ItemFrame *>(E); - for (int i = 0; i < frame->lines.size(); i++) { + float prev_h = 0; + for (int i = 0; i < (int)frame->lines.size(); i++) { + MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1; - _resize_line(frame, i, p_base_font, p_base_font_size, w); + prev_h = _resize_line(frame, i, p_base_font, p_base_font_size, w, prev_h); } idx++; } @@ -300,7 +304,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) { - table->columns.write[i].expand = true; + table->columns[i].expand = true; } if (table->columns[i].expand) { total_ratio += table->columns[i].expand_ratio; @@ -309,9 +313,9 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { - table->columns.write[i].width = table->columns[i].min_width; + table->columns[i].width = table->columns[i].min_width; if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { - table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; } @@ -328,7 +332,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> int dif = table->columns[i].width - table->columns[i].max_width; if (dif > 0) { table_need_fit = true; - table->columns.write[i].width = table->columns[i].max_width; + table->columns[i].width = table->columns[i].max_width; table->total_width -= dif; total_ratio -= table->columns[i].expand_ratio; } @@ -342,7 +346,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (dif > 0) { int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; int incr = MIN(dif, slice); - table->columns.write[i].width += incr; + table->columns[i].width += incr; table->total_width += incr; } } @@ -366,16 +370,14 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> offset.x += frame->padding.position.x; float yofs = frame->padding.position.y; - for (int i = 0; i < frame->lines.size(); i++) { - frame->lines.write[i].text_buf->set_width(table->columns[column].width); - table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); + float prev_h = 0; + for (int i = 0; i < (int)frame->lines.size(); i++) { + MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); + frame->lines[i].text_buf->set_width(table->columns[column].width); + table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x)); - if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } else { - frame->lines.write[i].offset.y = 0; - } - frame->lines.write[i].offset += offset; + frame->lines[i].offset.y = prev_h; + frame->lines[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); if (i > 0) { @@ -388,6 +390,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> h = MIN(h, frame->max_size_over.y); } yofs += h; + prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } yofs += frame->padding.size.y; offset.x += table->columns[column].width + hseparation + frame->padding.size.x; @@ -410,18 +413,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> } } - if (p_line > 0) { - l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } else { - l.offset.y = 0; - } + l.offset.y = p_h; + return l.offset.y + l.text_buf->get_size().y + l.text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } -void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset) { - ERR_FAIL_COND(p_frame == nullptr); - ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); +float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h, int *r_char_offset) { + ERR_FAIL_COND_V(p_frame == nullptr, p_h); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), p_h); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; + MutexLock lock(l.text_buf->get_mutex()); uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { @@ -458,7 +459,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Shape current paragraph. String text; - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; int remaining_characters = visible_characters - l.char_offset; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters <= 0) { @@ -524,9 +525,9 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> int t_char_count = 0; // Set minimums to zero. for (int i = 0; i < col_count; i++) { - table->columns.write[i].min_width = 0; - table->columns.write[i].max_width = 0; - table->columns.write[i].width = 0; + table->columns[i].min_width = 0; + table->columns[i].max_width = 0; + table->columns[i].width = 0; } // Compute minimum width for each cell. const int available_width = p_width - hseparation * (col_count - 1); @@ -537,17 +538,20 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> ItemFrame *frame = static_cast<ItemFrame *>(E); int column = idx % col_count; - for (int i = 0; i < frame->lines.size(); i++) { + float prev_h = 0; + for (int i = 0; i < (int)frame->lines.size(); i++) { + MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); + int char_offset = l.char_offset + l.char_count; int w = _find_margin(frame->lines[i].from, p_base_font, p_base_font_size) + 1; - _shape_line(frame, i, p_base_font, p_base_font_size, w, &char_offset); + prev_h = _shape_line(frame, i, p_base_font, p_base_font_size, w, prev_h, &char_offset); int cell_ch = (char_offset - (l.char_offset + l.char_count)); l.char_count += cell_ch; t_char_count += cell_ch; remaining_characters -= cell_ch; - table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x)); - table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x)); + table->columns[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x)); + table->columns[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wrapped_size().x)); } idx++; } @@ -560,7 +564,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) { - table->columns.write[i].expand = true; + table->columns[i].expand = true; } if (table->columns[i].expand) { total_ratio += table->columns[i].expand_ratio; @@ -569,9 +573,9 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { - table->columns.write[i].width = table->columns[i].min_width; + table->columns[i].width = table->columns[i].min_width; if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { - table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; } @@ -588,7 +592,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> int dif = table->columns[i].width - table->columns[i].max_width; if (dif > 0) { table_need_fit = true; - table->columns.write[i].width = table->columns[i].max_width; + table->columns[i].width = table->columns[i].max_width; table->total_width -= dif; total_ratio -= table->columns[i].expand_ratio; } @@ -602,7 +606,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (dif > 0) { int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; int incr = MIN(dif, slice); - table->columns.write[i].width += incr; + table->columns[i].width += incr; table->total_width += incr; } } @@ -626,16 +630,15 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> offset.x += frame->padding.position.x; float yofs = frame->padding.position.y; - for (int i = 0; i < frame->lines.size(); i++) { - frame->lines.write[i].text_buf->set_width(table->columns[column].width); - table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); + float prev_h = 0; + for (int i = 0; i < (int)frame->lines.size(); i++) { + MutexLock sub_lock(frame->lines[i].text_buf->get_mutex()); - if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + frame->lines[i - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } else { - frame->lines.write[i].offset.y = 0; - } - frame->lines.write[i].offset += offset; + frame->lines[i].text_buf->set_width(table->columns[column].width); + table->columns[column].width = MAX(table->columns[column].width, ceil(frame->lines[i].text_buf->get_size().x)); + + frame->lines[i].offset.y = prev_h; + frame->lines[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); if (i > 0) { @@ -648,6 +651,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> h = MIN(h, frame->max_size_over.y); } yofs += h; + prev_h = frame->lines[i].offset.y + frame->lines[i].text_buf->get_size().y + frame->lines[i].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } yofs += frame->padding.size.y; offset.x += table->columns[column].width + hseparation + frame->padding.size.x; @@ -678,24 +682,22 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> *r_char_offset = l.char_offset + l.char_count; - if (p_line > 0) { - l.offset.y = p_frame->lines[p_line - 1].offset.y + p_frame->lines[p_line - 1].text_buf->get_size().y + p_frame->lines[p_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } else { - l.offset.y = 0; - } + l.offset.y = p_h; + return l.offset.y + l.text_buf->get_size().y + l.text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) { ERR_FAIL_COND_V(p_frame == nullptr, 0); - ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), 0); Vector2 off; int line_spacing = get_theme_constant(SNAME("line_separation")); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; + MutexLock lock(l.text_buf->get_mutex()); Item *it_from = l.from; - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; if (it_from == nullptr) { return 0; @@ -877,7 +879,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o draw_rect(Rect2(p_ofs + rect.position + off + coff - frame->padding.position, Size2(table->columns[col].width + hseparation + frame->padding.position.x + frame->padding.size.x, table->rows[row])), (frame->border != Color(0, 0, 0, 0) ? frame->border : border), false); } - for (int j = 0; j < frame->lines.size(); j++) { + for (int j = 0; j < (int)frame->lines.size(); j++) { _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs); } idx++; @@ -1299,22 +1301,12 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item int vofs = vscroll->get_value(); // Search for the first line. - int from_line = 0; - - //TODO, change to binary search ? - while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) { - break; - } - from_line++; - } - - if (from_line >= main->lines.size()) { - return; - } + int to_line = main->first_invalid_line.load(); + int from_line = _find_first_line(0, to_line, vofs); Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); - while (ofs.y < size.height && from_line < main->lines.size()) { + while (ofs.y < size.height && from_line < to_line) { + MutexLock lock(main->lines[from_line].text_buf->get_mutex()); _find_click_in_line(p_frame, from_line, ofs, text_rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { @@ -1331,7 +1323,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V Vector2 off; int char_pos = -1; - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; + MutexLock lock(l.text_buf->get_mutex()); + bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); bool lrtl = is_layout_rtl(); @@ -1420,14 +1414,14 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } } if (crect.has_point(p_click)) { - for (int j = 0; j < frame->lines.size(); j++) { + for (int j = 0; j < (int)frame->lines.size(); j++) { _find_click_in_line(frame, j, rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, &table_click_frame, &table_click_line, &table_click_item, &table_click_char, true); if (table_click_frame && table_click_item) { // Save cell detected cell hit data. table_range = Vector2i(INT32_MAX, 0); for (Item *F : table->subitems) { ItemFrame *sub_frame = static_cast<ItemFrame *>(F); - for (int k = 0; k < sub_frame->lines.size(); k++) { + for (int k = 0; k < (int)sub_frame->lines.size(); k++) { table_range.x = MIN(table_range.x, sub_frame->lines[k].char_offset); table_range.y = MAX(table_range.y, sub_frame->lines[k].char_offset + sub_frame->lines[k].char_count); } @@ -1484,7 +1478,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V // Find item. if (r_click_item != nullptr) { Item *it = p_frame->lines[p_line].from; - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; if (char_pos == p_frame->lines[p_line].char_count) { // Selection after the end of line, select last item. if (it_to != nullptr) { @@ -1532,28 +1526,6 @@ void RichTextLabel::_scroll_changed(double) { update(); } -void RichTextLabel::_update_scroll() { - int total_height = get_content_height(); - - bool exceeds = total_height > get_size().height && scroll_active; - - if (exceeds != scroll_visible) { - if (exceeds) { - scroll_visible = true; - scroll_w = vscroll->get_combined_minimum_size().width; - vscroll->show(); - vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w); - } else { - scroll_visible = false; - scroll_w = 0; - vscroll->hide(); - } - - main->first_resized_line = 0; //invalidate ALL - _validate_line_caches(main); - } -} - void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta_time) { Item *it = p_frame; while (it) { @@ -1588,6 +1560,22 @@ void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, double p_delta } } +int RichTextLabel::_find_first_line(int p_from, int p_to, int p_vofs) const { + int l = p_from; + int r = p_to; + while (l < r) { + int m = Math::floor(double(l + r) / 2.0); + MutexLock lock(main->lines[m].text_buf->get_mutex()); + int ofs = main->lines[m].offset.y + main->lines[m].text_buf->get_size().y + main->lines[m].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + if (ofs < p_vofs) { + l = m + 1; + } else { + r = m; + } + } + return l; +} + void RichTextLabel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_MOUSE_EXIT: { @@ -1600,38 +1588,45 @@ void RichTextLabel::_notification(int p_what) { } break; case NOTIFICATION_RESIZED: { - main->first_resized_line = 0; //invalidate ALL + _stop_thread(); + main->first_resized_line.store(0); //invalidate ALL update(); } break; case NOTIFICATION_THEME_CHANGED: { - main->first_invalid_font_line = 0; //invalidate ALL + _stop_thread(); + main->first_invalid_font_line.store(0); //invalidate ALL update(); } break; case NOTIFICATION_ENTER_TREE: { + _stop_thread(); if (!text.is_empty()) { set_text(text); } - main->first_invalid_line = 0; //invalidate ALL + main->first_invalid_line.store(0); //invalidate ALL update(); } break; + case NOTIFICATION_EXIT_TREE: { + _stop_thread(); + } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - main->first_invalid_line = 0; //invalidate ALL + _stop_thread(); + main->first_invalid_line.store(0); //invalidate ALL update(); } break; - case NOTIFICATION_DRAW: { - _validate_line_caches(main); - _update_scroll(); + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + update(); + } break; + case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); - Size2 size = get_size(); - Rect2 text_rect = _get_text_rect(); draw_style_box(get_theme_stylebox(SNAME("normal")), Rect2(Point2(), size)); @@ -1641,22 +1636,42 @@ void RichTextLabel::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); } + // Start text shaping. + if (_validate_line_caches()) { + set_physics_process_internal(false); // Disable auto refresh, if text is fully processed. + } else { + // Draw loading progress bar. + if ((progress_delay > 0) && (OS::get_singleton()->get_ticks_msec() - loading_started >= (uint64_t)progress_delay)) { + Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"), SNAME("ProgressBar")); + Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"), SNAME("ProgressBar")); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + + Vector2 p_size = Vector2(size.width - (style->get_offset().x + vscroll->get_combined_minimum_size().width) * 2, vscroll->get_combined_minimum_size().width); + Vector2 p_pos = Vector2(style->get_offset().x, size.height - style->get_offset().y - vscroll->get_combined_minimum_size().width); + + draw_style_box(bg, Rect2(p_pos, p_size)); + + bool right_to_left = is_layout_rtl(); + double r = loaded.load(); + int mp = fg->get_minimum_size().width; + int p = round(r * (p_size.width - mp)); + if (right_to_left) { + int p_remaining = round((1.0 - r) * (p_size.width - mp)); + draw_style_box(fg, Rect2(p_pos + Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, p_size.height))); + } else { + draw_style_box(fg, Rect2(p_pos, Size2(p + fg->get_minimum_size().width, p_size.height))); + } + } + } + + // Draw main text. + Rect2 text_rect = _get_text_rect(); float vofs = vscroll->get_value(); // Search for the first line. - int from_line = 0; - - //TODO, change to binary search ? - while (from_line < main->lines.size()) { - if (main->lines[from_line].offset.y + main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")) >= vofs) { - break; - } - from_line++; - } + int to_line = main->first_invalid_line.load(); + int from_line = _find_first_line(0, to_line, vofs); - if (from_line >= main->lines.size()) { - break; //nothing to draw - } Ref<Font> base_font = get_theme_font(SNAME("normal_font")); Color base_color = get_theme_color(SNAME("default_color")); Color outline_color = get_theme_color(SNAME("font_outline_color")); @@ -1671,7 +1686,9 @@ void RichTextLabel::_notification(int p_what) { // New cache draw. Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs); int processed_glyphs = 0; - while (ofs.y < size.height && from_line < main->lines.size()) { + while (ofs.y < size.height && from_line < to_line) { + MutexLock lock(main->lines[from_line].text_buf->get_mutex()); + visible_paragraph_count++; visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs); ofs.y += main->lines[from_line].text_buf->get_size().y + main->lines[from_line].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); @@ -1681,6 +1698,9 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (is_visible_in_tree()) { + if (!is_ready()) { + return; + } double dt = get_process_delta_time(); _update_fx(main, dt); update(); @@ -1708,18 +1728,6 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const return CURSOR_IBEAM; } - if (main->first_invalid_line < main->lines.size()) { - return get_default_cursor_shape(); //invalid - } - - if (main->first_invalid_font_line < main->lines.size()) { - return get_default_cursor_shape(); //invalid - } - - if (main->first_resized_line < main->lines.size()) { - return get_default_cursor_shape(); //invalid - } - Item *item = nullptr; bool outside = true; const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); @@ -1727,7 +1735,6 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) { return CURSOR_POINTING_HAND; } - return get_default_cursor_shape(); } @@ -1737,16 +1744,6 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - if (main->first_invalid_line < main->lines.size()) { - return; - } - if (main->first_invalid_font_line < main->lines.size()) { - return; - } - if (main->first_resized_line < main->lines.size()) { - return; - } - if (b->get_button_index() == MouseButton::LEFT) { if (b->is_pressed() && !b->is_double_click()) { scroll_updated = false; @@ -1800,6 +1797,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (c_frame) { const Line &l = c_frame->lines[c_line]; + MutexLock lock(l.text_buf->get_mutex()); PackedInt32Array words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid()); for (int i = 0; i < words.size(); i = i + 2) { if (c_index >= words[i] && c_index < words[i + 1]) { @@ -1945,18 +1943,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseMotion> m = p_event; - if (m.is_valid()) { - if (main->first_invalid_line < main->lines.size()) { - return; - } - if (main->first_invalid_font_line < main->lines.size()) { - return; - } - if (main->first_resized_line < main->lines.size()) { - return; - } - ItemFrame *c_frame = nullptr; int c_line = 0; Item *c_item = nullptr; @@ -2434,93 +2421,215 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { return false; } -void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { - if (p_frame->first_invalid_line == p_frame->lines.size()) { +void RichTextLabel::_thread_function(void *self) { + RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self); + rtl->_process_line_caches(); + rtl->updating.store(false); + rtl->call_deferred(SNAME("update")); +} + +void RichTextLabel::_stop_thread() { + if (threaded) { + stop_thread.store(true); + thread.wait_to_finish(); + } +} + +bool RichTextLabel::is_ready() const { + if (updating.load()) { + return false; + } + return (main->first_invalid_line.load() == (int)main->lines.size() && main->first_resized_line.load() == (int)main->lines.size() && main->first_invalid_font_line.load() == (int)main->lines.size()); +} + +void RichTextLabel::set_threaded(bool p_threaded) { + if (threaded != p_threaded) { + _stop_thread(); + threaded = p_threaded; + update(); + } +} + +bool RichTextLabel::is_threaded() const { + return threaded; +} + +void RichTextLabel::set_progress_bar_delay(int p_delay_ms) { + progress_delay = p_delay_ms; +} + +int RichTextLabel::get_progress_bar_delay() const { + return progress_delay; +} + +bool RichTextLabel::_validate_line_caches() { + if (updating.load()) { + return false; + } + if (main->first_invalid_line.load() == (int)main->lines.size()) { + MutexLock data_lock(data_mutex); + Rect2 text_rect = _get_text_rect(); + Ref<Font> base_font = get_theme_font(SNAME("normal_font")); int base_font_size = get_theme_font_size(SNAME("normal_font_size")); + int ctrl_height = get_size().height; // Update fonts. - if (p_frame->first_invalid_font_line != p_frame->lines.size()) { - for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) { - _update_line_font(p_frame, i, base_font, base_font_size); + if (main->first_invalid_font_line.load() != (int)main->lines.size()) { + for (int i = main->first_invalid_font_line.load(); i < (int)main->lines.size(); i++) { + _update_line_font(main, i, base_font, base_font_size); } - p_frame->first_resized_line = p_frame->first_invalid_font_line; - p_frame->first_invalid_font_line = p_frame->lines.size(); + main->first_resized_line.store(main->first_invalid_font_line.load()); + main->first_invalid_font_line.store(main->lines.size()); } - if (p_frame->first_resized_line == p_frame->lines.size()) { - return; + if (main->first_resized_line.load() == (int)main->lines.size()) { + return true; } // Resize lines without reshaping. - Rect2 text_rect = _get_text_rect(); + int fi = main->first_resized_line.load(); + + float total_height = 0; + for (int i = fi; i < (int)main->lines.size(); i++) { + total_height = _resize_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + + updating_scroll = true; + bool exceeds = total_height > ctrl_height && scroll_active; + if (exceeds != scroll_visible) { + if (exceeds) { + scroll_visible = true; + scroll_w = vscroll->get_combined_minimum_size().width; + vscroll->show(); + vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w); + } else { + scroll_visible = false; + scroll_w = 0; + vscroll->hide(); + } - for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) { - _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w); - } + main->first_resized_line.store(0); - int total_height = 0; - if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } + total_height = 0; + for (int j = 0; j <= i; j++) { + total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + + main->first_resized_line.store(j); + } + } - p_frame->first_resized_line = p_frame->lines.size(); + vscroll->set_max(total_height); + vscroll->set_page(text_rect.size.height); + if (scroll_follow && scroll_following) { + vscroll->set_value(total_height); + } + updating_scroll = false; - updating_scroll = true; - vscroll->set_max(total_height); - vscroll->set_page(text_rect.size.height); - if (scroll_follow && scroll_following) { - vscroll->set_value(total_height); + main->first_resized_line.store(i); } - updating_scroll = false; + + main->first_resized_line.store(main->lines.size()); if (fit_content_height) { update_minimum_size(); } - return; + return true; } + stop_thread.store(false); + if (threaded) { + updating.store(true); + loaded.store(true); + thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this)); + loading_started = OS::get_singleton()->get_ticks_msec(); + set_physics_process_internal(true); + return false; + } else { + _process_line_caches(); + update(); + return true; + } +} +void RichTextLabel::_process_line_caches() { // Shape invalid lines. + MutexLock data_lock(data_mutex); Rect2 text_rect = _get_text_rect(); Ref<Font> base_font = get_theme_font(SNAME("normal_font")); int base_font_size = get_theme_font_size(SNAME("normal_font_size")); + int ctrl_height = get_size().height; + int fi = main->first_invalid_line.load(); + int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count); - int total_chars = (p_frame->first_invalid_line == 0) ? 0 : (p_frame->lines[p_frame->first_invalid_line].char_offset + p_frame->lines[p_frame->first_invalid_line].char_count); - for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) { - _shape_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, &total_chars); - } + float total_height = 0; + for (int i = fi; i < (int)main->lines.size(); i++) { + total_height = _shape_line(main, i, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height, &total_chars); - int total_height = 0; - if (p_frame->lines.size()) { - total_height = p_frame->lines[p_frame->lines.size() - 1].offset.y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_size().y + p_frame->lines[p_frame->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); - } + updating_scroll = true; + bool exceeds = total_height > ctrl_height && scroll_active; + if (exceeds != scroll_visible) { + if (exceeds) { + scroll_visible = true; + scroll_w = vscroll->get_combined_minimum_size().width; + vscroll->show(); + vscroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_w); + } else { + scroll_visible = false; + scroll_w = 0; + vscroll->hide(); + } - p_frame->first_invalid_line = p_frame->lines.size(); - p_frame->first_resized_line = p_frame->lines.size(); - p_frame->first_invalid_font_line = p_frame->lines.size(); + main->first_invalid_line.store(0); + main->first_resized_line.store(0); + main->first_invalid_font_line.store(0); - updating_scroll = true; - vscroll->set_max(total_height); - vscroll->set_page(text_rect.size.height); - if (scroll_follow && scroll_following) { - vscroll->set_value(total_height); + total_height = 0; + for (int j = 0; j <= i; j++) { + total_height = _resize_line(main, j, base_font, base_font_size, text_rect.get_size().width - scroll_w, total_height); + + main->first_invalid_line.store(j); + main->first_resized_line.store(j); + main->first_invalid_font_line.store(j); + } + } + + vscroll->set_max(total_height); + vscroll->set_page(text_rect.size.height); + if (scroll_follow && scroll_following) { + vscroll->set_value(total_height); + } + updating_scroll = false; + + main->first_invalid_line.store(i); + main->first_resized_line.store(i); + main->first_invalid_font_line.store(i); + + if (stop_thread.load()) { + return; + } + loaded.store(double(i) / double(main->lines.size())); } - updating_scroll = false; + + main->first_invalid_line.store(main->lines.size()); + main->first_resized_line.store(main->lines.size()); + main->first_invalid_font_line.store(main->lines.size()); if (fit_content_height) { update_minimum_size(); } + emit_signal(SNAME("finished")); } void RichTextLabel::_invalidate_current_line(ItemFrame *p_frame) { - if (p_frame->lines.size() - 1 <= p_frame->first_invalid_line) { - p_frame->first_invalid_line = p_frame->lines.size() - 1; - update(); + if ((int)p_frame->lines.size() - 1 <= p_frame->first_invalid_line) { + p_frame->first_invalid_line = (int)p_frame->lines.size() - 1; } } void RichTextLabel::add_text(const String &p_text) { + _stop_thread(); + MutexLock data_lock(data_mutex); + if (current->type == ITEM_TABLE) { return; //can't add anything here } @@ -2564,13 +2673,14 @@ void RichTextLabel::add_text(const String &p_text) { _add_item(item, false); current_frame->lines.resize(current_frame->lines.size() + 1); if (item->type != ITEM_NEWLINE) { - current_frame->lines.write[current_frame->lines.size() - 1].from = item; + current_frame->lines[current_frame->lines.size() - 1].from = item; } _invalidate_current_line(current_frame); } pos = end + 1; } + update(); } void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) { @@ -2599,7 +2709,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) } if (current_frame->lines[current_frame->lines.size() - 1].from == nullptr) { - current_frame->lines.write[current_frame->lines.size() - 1].from = p_item; + current_frame->lines[current_frame->lines.size() - 1].from = p_item; } p_item->line = current_frame->lines.size() - 1; @@ -2608,6 +2718,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) if (fixed_width != -1) { update_minimum_size(); } + update(); } void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_subitem_line) { @@ -2635,6 +2746,9 @@ 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, InlineAlignment p_alignment) { + _stop_thread(); + MutexLock data_lock(data_mutex); + if (current->type == ITEM_TABLE) { return; } @@ -2674,6 +2788,9 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, } void RichTextLabel::add_newline() { + _stop_thread(); + MutexLock data_lock(data_mutex); + if (current->type == ITEM_TABLE) { return; } @@ -2682,10 +2799,14 @@ void RichTextLabel::add_newline() { _add_item(item, false); current_frame->lines.resize(current_frame->lines.size() + 1); _invalidate_current_line(current_frame); + update(); } bool RichTextLabel::remove_line(const int p_line) { - if (p_line >= current_frame->lines.size() || p_line < 0) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + if (p_line >= (int)current_frame->lines.size() || p_line < 0) { return false; } @@ -2713,16 +2834,19 @@ bool RichTextLabel::remove_line(const int p_line) { } if (p_line == 0 && current->subitems.size() > 0) { - main->lines.write[0].from = main; + main->lines[0].from = main; } - main->first_invalid_line = 0; // p_line ??? + main->first_invalid_line.store(0); update(); return true; } void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_string.is_empty()); ERR_FAIL_COND(p_font.is_null()); @@ -2741,6 +2865,9 @@ void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font } void RichTextLabel::push_font(const Ref<Font> &p_font) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_font.is_null()); ItemFont *item = memnew(ItemFont); @@ -2785,6 +2912,9 @@ void RichTextLabel::push_mono() { } void RichTextLabel::push_font_size(int p_font_size) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemFontSize *item = memnew(ItemFontSize); @@ -2793,6 +2923,9 @@ void RichTextLabel::push_font_size(int p_font_size) { } void RichTextLabel::push_font_features(const Dictionary &p_features) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemFontFeatures *item = memnew(ItemFontFeatures); @@ -2801,6 +2934,9 @@ void RichTextLabel::push_font_features(const Dictionary &p_features) { } void RichTextLabel::push_outline_size(int p_font_size) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemOutlineSize *item = memnew(ItemOutlineSize); @@ -2809,6 +2945,9 @@ void RichTextLabel::push_outline_size(int p_font_size) { } void RichTextLabel::push_color(const Color &p_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemColor *item = memnew(ItemColor); @@ -2817,6 +2956,9 @@ void RichTextLabel::push_color(const Color &p_color) { } void RichTextLabel::push_outline_color(const Color &p_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemOutlineColor *item = memnew(ItemOutlineColor); @@ -2825,6 +2967,9 @@ void RichTextLabel::push_outline_color(const Color &p_color) { } void RichTextLabel::push_underline() { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemUnderline *item = memnew(ItemUnderline); @@ -2832,6 +2977,9 @@ void RichTextLabel::push_underline() { } void RichTextLabel::push_strikethrough() { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemStrikethrough *item = memnew(ItemStrikethrough); @@ -2839,6 +2987,9 @@ void RichTextLabel::push_strikethrough() { } void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemParagraph *item = memnew(ItemParagraph); @@ -2850,6 +3001,9 @@ void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::Tex } void RichTextLabel::push_indent(int p_level) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_level < 0); @@ -2859,6 +3013,9 @@ void RichTextLabel::push_indent(int p_level) { } void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(p_level < 0); @@ -2871,6 +3028,9 @@ void RichTextLabel::push_list(int p_level, ListType p_list, bool p_capitalize) { } void RichTextLabel::push_meta(const Variant &p_meta) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemMeta *item = memnew(ItemMeta); @@ -2879,6 +3039,9 @@ void RichTextLabel::push_meta(const Variant &p_meta) { } void RichTextLabel::push_hint(const String &p_string) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemHint *item = memnew(ItemHint); @@ -2887,20 +3050,26 @@ void RichTextLabel::push_hint(const String &p_string) { } void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(p_columns < 1); ItemTable *item = memnew(ItemTable); item->columns.resize(p_columns); item->total_width = 0; item->inline_align = p_alignment; - for (int i = 0; i < item->columns.size(); i++) { - item->columns.write[i].expand = false; - item->columns.write[i].expand_ratio = 1; + for (int i = 0; i < (int)item->columns.size(); i++) { + item->columns[i].expand = false; + item->columns[i].expand_ratio = 1; } _add_item(item, true, false); } void RichTextLabel::push_fade(int p_start_index, int p_length) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemFade *item = memnew(ItemFade); item->starting_index = p_start_index; item->length = p_length; @@ -2908,6 +3077,9 @@ void RichTextLabel::push_fade(int p_start_index, int p_length) { } void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemShake *item = memnew(ItemShake); item->strength = p_strength; item->rate = p_rate; @@ -2915,6 +3087,9 @@ void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) { } void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemWave *item = memnew(ItemWave); item->frequency = p_frequency; item->amplitude = p_amplitude; @@ -2922,6 +3097,9 @@ void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0 } void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemTornado *item = memnew(ItemTornado); item->frequency = p_frequency; item->radius = p_radius; @@ -2929,6 +3107,9 @@ void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0 } void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemRainbow *item = memnew(ItemRainbow); item->frequency = p_frequency; item->saturation = p_saturation; @@ -2937,6 +3118,9 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq } void RichTextLabel::push_bgcolor(const Color &p_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemBGColor *item = memnew(ItemBGColor); @@ -2945,6 +3129,9 @@ void RichTextLabel::push_bgcolor(const Color &p_color) { } void RichTextLabel::push_fgcolor(const Color &p_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type == ITEM_TABLE); ItemFGColor *item = memnew(ItemFGColor); @@ -2953,6 +3140,9 @@ void RichTextLabel::push_fgcolor(const Color &p_color) { } void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ItemCustomFX *item = memnew(ItemCustomFX); item->custom_effect = p_custom_effect; item->char_fx_transform->environment = p_environment; @@ -2960,15 +3150,23 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar } void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_TABLE); + ItemTable *table = static_cast<ItemTable *>(current); - ERR_FAIL_INDEX(p_column, table->columns.size()); - table->columns.write[p_column].expand = p_expand; - table->columns.write[p_column].expand_ratio = p_ratio; + ERR_FAIL_INDEX(p_column, (int)table->columns.size()); + table->columns[p_column].expand = p_expand; + table->columns[p_column].expand_ratio = p_ratio; } void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); ERR_FAIL_COND(!cell->cell); cell->odd_row_bg = p_odd_row_bg; @@ -2976,14 +3174,22 @@ void RichTextLabel::set_cell_row_background_color(const Color &p_odd_row_bg, con } void RichTextLabel::set_cell_border_color(const Color &p_color) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); ERR_FAIL_COND(!cell->cell); cell->border = p_color; } void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); ERR_FAIL_COND(!cell->cell); cell->min_size_over = p_min_size; @@ -2991,13 +3197,20 @@ void RichTextLabel::set_cell_size_override(const Size2 &p_min_size, const Size2 } void RichTextLabel::set_cell_padding(const Rect2 &p_padding) { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_FRAME); + ItemFrame *cell = static_cast<ItemFrame *>(current); ERR_FAIL_COND(!cell->cell); cell->padding = p_padding; } void RichTextLabel::push_cell() { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(current->type != ITEM_TABLE); ItemFrame *item = memnew(ItemFrame); @@ -3006,20 +3219,23 @@ void RichTextLabel::push_cell() { current_frame = item; item->cell = true; item->lines.resize(1); - item->lines.write[0].from = nullptr; - item->first_invalid_line = 0; // parent frame last line ??? + item->lines[0].from = nullptr; + item->first_invalid_line.store(0); // parent frame last line ??? } int RichTextLabel::get_current_table_column() const { ERR_FAIL_COND_V(current->type != ITEM_TABLE, -1); ItemTable *table = static_cast<ItemTable *>(current); - return table->subitems.size() % table->columns.size(); } void RichTextLabel::pop() { + _stop_thread(); + MutexLock data_lock(data_mutex); + ERR_FAIL_COND(!current->parent); + if (current->type == ITEM_FRAME) { current_frame = static_cast<ItemFrame *>(current)->parent_frame; } @@ -3027,12 +3243,15 @@ void RichTextLabel::pop() { } void RichTextLabel::clear() { + _stop_thread(); + MutexLock data_lock(data_mutex); + main->_clear_children(); current = main; current_frame = main; main->lines.clear(); main->lines.resize(1); - main->first_invalid_line = 0; + main->first_invalid_line.store(0); selection.click_frame = nullptr; selection.click_item = nullptr; @@ -3050,8 +3269,10 @@ void RichTextLabel::clear() { } void RichTextLabel::set_tab_size(int p_spaces) { + _stop_thread(); + tab_size = p_spaces; - main->first_resized_line = 0; + main->first_resized_line.store(0); update(); } @@ -3131,6 +3352,9 @@ void RichTextLabel::parse_bbcode(const String &p_bbcode) { } void RichTextLabel::append_text(const String &p_bbcode) { + _stop_thread(); + MutexLock data_lock(data_mutex); + int pos = 0; List<String> tag_stack; @@ -3177,7 +3401,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { // Find optional parameters. String bbcode_name; - typedef Map<String, String> OptionMap; + typedef HashMap<String, String> OptionMap; OptionMap bbcode_options; if (!split_tag_block.is_empty()) { bbcode_name = split_tag_block[0]; @@ -3620,9 +3844,9 @@ void RichTextLabel::append_text(const String &p_bbcode) { Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D"); if (texture.is_valid()) { Color color = Color(1.0, 1.0, 1.0); - OptionMap::Element *color_option = bbcode_options.find("color"); + OptionMap::Iterator color_option = bbcode_options.find("color"); if (color_option) { - color = Color::from_string(color_option->value(), color); + color = Color::from_string(color_option->value, color); } int width = 0; @@ -3636,14 +3860,14 @@ void RichTextLabel::append_text(const String &p_bbcode) { height = bbcode_value.substr(sep + 1).to_int(); } } else { - OptionMap::Element *width_option = bbcode_options.find("width"); + OptionMap::Iterator width_option = bbcode_options.find("width"); if (width_option) { - width = width_option->value().to_int(); + width = width_option->value.to_int(); } - OptionMap::Element *height_option = bbcode_options.find("height"); + OptionMap::Iterator height_option = bbcode_options.find("height"); if (height_option) { - height = height_option->value().to_int(); + height = height_option->value.to_int(); } } @@ -3729,15 +3953,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { } else if (bbcode_name == "fade") { int start_index = 0; - OptionMap::Element *start_option = bbcode_options.find("start"); + OptionMap::Iterator start_option = bbcode_options.find("start"); if (start_option) { - start_index = start_option->value().to_int(); + start_index = start_option->value.to_int(); } int length = 10; - OptionMap::Element *length_option = bbcode_options.find("length"); + OptionMap::Iterator length_option = bbcode_options.find("length"); if (length_option) { - length = length_option->value().to_int(); + length = length_option->value.to_int(); } push_fade(start_index, length); @@ -3745,15 +3969,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { tag_stack.push_front("fade"); } else if (bbcode_name == "shake") { int strength = 5; - OptionMap::Element *strength_option = bbcode_options.find("level"); + OptionMap::Iterator strength_option = bbcode_options.find("level"); if (strength_option) { - strength = strength_option->value().to_int(); + strength = strength_option->value.to_int(); } float rate = 20.0f; - OptionMap::Element *rate_option = bbcode_options.find("rate"); + OptionMap::Iterator rate_option = bbcode_options.find("rate"); if (rate_option) { - rate = rate_option->value().to_float(); + rate = rate_option->value.to_float(); } push_shake(strength, rate); @@ -3762,15 +3986,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { set_process_internal(true); } else if (bbcode_name == "wave") { float amplitude = 20.0f; - OptionMap::Element *amplitude_option = bbcode_options.find("amp"); + OptionMap::Iterator amplitude_option = bbcode_options.find("amp"); if (amplitude_option) { - amplitude = amplitude_option->value().to_float(); + amplitude = amplitude_option->value.to_float(); } float period = 5.0f; - OptionMap::Element *period_option = bbcode_options.find("freq"); + OptionMap::Iterator period_option = bbcode_options.find("freq"); if (period_option) { - period = period_option->value().to_float(); + period = period_option->value.to_float(); } push_wave(period, amplitude); @@ -3779,15 +4003,15 @@ void RichTextLabel::append_text(const String &p_bbcode) { set_process_internal(true); } else if (bbcode_name == "tornado") { float radius = 10.0f; - OptionMap::Element *radius_option = bbcode_options.find("radius"); + OptionMap::Iterator radius_option = bbcode_options.find("radius"); if (radius_option) { - radius = radius_option->value().to_float(); + radius = radius_option->value.to_float(); } float frequency = 1.0f; - OptionMap::Element *frequency_option = bbcode_options.find("freq"); + OptionMap::Iterator frequency_option = bbcode_options.find("freq"); if (frequency_option) { - frequency = frequency_option->value().to_float(); + frequency = frequency_option->value.to_float(); } push_tornado(frequency, radius); @@ -3796,21 +4020,21 @@ void RichTextLabel::append_text(const String &p_bbcode) { set_process_internal(true); } else if (bbcode_name == "rainbow") { float saturation = 0.8f; - OptionMap::Element *saturation_option = bbcode_options.find("sat"); + OptionMap::Iterator saturation_option = bbcode_options.find("sat"); if (saturation_option) { - saturation = saturation_option->value().to_float(); + saturation = saturation_option->value.to_float(); } float value = 0.8f; - OptionMap::Element *value_option = bbcode_options.find("val"); + OptionMap::Iterator value_option = bbcode_options.find("val"); if (value_option) { - value = value_option->value().to_float(); + value = value_option->value.to_float(); } float frequency = 1.0f; - OptionMap::Element *frequency_option = bbcode_options.find("freq"); + OptionMap::Iterator frequency_option = bbcode_options.find("freq"); if (frequency_option) { - frequency = frequency_option->value().to_float(); + frequency = frequency_option->value.to_float(); } push_rainbow(saturation, value, frequency); @@ -3871,9 +4095,13 @@ void RichTextLabel::append_text(const String &p_bbcode) { } void RichTextLabel::scroll_to_paragraph(int p_paragraph) { - ERR_FAIL_INDEX(p_paragraph, main->lines.size()); - _validate_line_caches(main); - vscroll->set_value(main->lines[p_paragraph].offset.y); + if (p_paragraph <= 0) { + vscroll->set_value(0); + } else if (p_paragraph >= main->first_invalid_line.load()) { + vscroll->set_value(vscroll->get_max()); + } else { + vscroll->set_value(main->lines[p_paragraph].offset.y); + } } int RichTextLabel::get_paragraph_count() const { @@ -3888,10 +4116,14 @@ int RichTextLabel::get_visible_paragraph_count() const { } void RichTextLabel::scroll_to_line(int p_line) { - _validate_line_caches(main); - + if (p_line <= 0) { + vscroll->set_value(0); + return; + } int line_count = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { + MutexLock lock(main->lines[i].text_buf->get_mutex()); if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) { float line_offset = 0.f; for (int j = 0; j < p_line - line_count; j++) { @@ -3902,11 +4134,14 @@ void RichTextLabel::scroll_to_line(int p_line) { } line_count += main->lines[i].text_buf->get_line_count(); } + vscroll->set_value(vscroll->get_max()); } float RichTextLabel::get_line_offset(int p_line) { int line_count = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { + MutexLock lock(main->lines[i].text_buf->get_mutex()); if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) { float line_offset = 0.f; for (int j = 0; j < p_line - line_count; j++) { @@ -3920,7 +4155,8 @@ float RichTextLabel::get_line_offset(int p_line) { } float RichTextLabel::get_paragraph_offset(int p_paragraph) { - if (0 <= p_paragraph && p_paragraph < main->lines.size()) { + int to_line = main->first_invalid_line.load(); + if (0 <= p_paragraph && p_paragraph < to_line) { return main->lines[p_paragraph].offset.y; } return 0; @@ -3928,7 +4164,9 @@ float RichTextLabel::get_paragraph_offset(int p_paragraph) { int RichTextLabel::get_line_count() const { int line_count = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { + MutexLock lock(main->lines[i].text_buf->get_mutex()); line_count += main->lines[i].text_buf->get_line_count(); } return line_count; @@ -3989,13 +4227,13 @@ bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_f ERR_CONTINUE(E->get()->type != ITEM_FRAME); // Children should all be frames. ItemFrame *frame = static_cast<ItemFrame *>(E->get()); if (p_reverse_search) { - for (int i = frame->lines.size() - 1; i >= 0; i--) { + for (int i = (int)frame->lines.size() - 1; i >= 0; i--) { if (_search_line(frame, i, p_string, -1, p_reverse_search)) { return true; } } } else { - for (int i = 0; i < frame->lines.size(); i++) { + for (int i = 0; i < (int)frame->lines.size(); i++) { if (_search_line(frame, i, p_string, 0, p_reverse_search)) { return true; } @@ -4008,12 +4246,12 @@ bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_f bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search) { ERR_FAIL_COND_V(p_frame == nullptr, false); - ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), false); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), false); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; String text; - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { switch (it->type) { case ITEM_NEWLINE: { @@ -4071,7 +4309,8 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p int char_idx = p_search_previous ? -1 : 0; int current_line = 0; - int ending_line = main->lines.size() - 1; + int to_line = main->first_invalid_line.load(); + int ending_line = to_line - 1; if (p_from_selection && selection.active) { // First check to see if other results exist in current line char_idx = p_search_previous ? selection.from_char - 1 : selection.to_char; @@ -4120,8 +4359,8 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p while (current_line != ending_line) { // Wrap around if (current_line < 0) { - current_line = main->lines.size() - 1; - } else if (current_line >= main->lines.size()) { + current_line = to_line - 1; + } else if (current_line >= to_line) { current_line = 0; } @@ -4143,12 +4382,13 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p_selection) const { String text; + ERR_FAIL_COND_V(p_frame == nullptr, text); - ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), text); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)p_frame->lines.size(), text); - Line &l = p_frame->lines.write[p_line]; + Line &l = p_frame->lines[p_line]; - Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; int end_idx = 0; if (it_to != nullptr) { end_idx = it_to->index; @@ -4163,7 +4403,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p for (Item *E : table->subitems) { ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. ItemFrame *frame = static_cast<ItemFrame *>(E); - for (int i = 0; i < frame->lines.size(); i++) { + for (int i = 0; i < (int)frame->lines.size(); i++) { text += _get_line_text(frame, i, p_selection); } } @@ -4227,7 +4467,8 @@ String RichTextLabel::get_selected_text() const { } String text; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { text += _get_line_text(main, i, selection); } return text; @@ -4368,19 +4609,23 @@ String RichTextLabel::get_parsed_text() const { void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + _stop_thread(); + if (text_direction != p_text_direction) { text_direction = p_text_direction; - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); update(); } } void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { + _stop_thread(); + st_parser = p_parser; - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); update(); } } @@ -4390,10 +4635,14 @@ TextServer::StructuredTextParser RichTextLabel::get_structured_text_bidi_overrid } void RichTextLabel::set_structured_text_bidi_override_options(Array p_args) { - st_args = p_args; - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); - update(); + if (st_args != p_args) { + _stop_thread(); + + st_args = p_args; + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); + update(); + } } Array RichTextLabel::get_structured_text_bidi_override_options() const { @@ -4406,9 +4655,11 @@ Control::TextDirection RichTextLabel::get_text_direction() const { void RichTextLabel::set_language(const String &p_language) { if (language != p_language) { + _stop_thread(); + language = p_language; - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); update(); } } @@ -4419,9 +4670,11 @@ String RichTextLabel::get_language() const { void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) { if (autowrap_mode != p_mode) { + _stop_thread(); + autowrap_mode = p_mode; main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + _validate_line_caches(); update(); } } @@ -4432,6 +4685,8 @@ RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const { void RichTextLabel::set_percent_visible(float p_percent) { if (percent_visible != p_percent) { + _stop_thread(); + if (p_percent < 0 || p_percent >= 1) { visible_characters = -1; percent_visible = 1; @@ -4440,8 +4695,8 @@ void RichTextLabel::set_percent_visible(float p_percent) { percent_visible = p_percent; } if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) { - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); } update(); } @@ -4476,15 +4731,19 @@ void RichTextLabel::install_effect(const Variant effect) { int RichTextLabel::get_content_height() const { int total_height = 0; - if (main->lines.size()) { - total_height = main->lines[main->lines.size() - 1].offset.y + main->lines[main->lines.size() - 1].text_buf->get_size().y + main->lines[main->lines.size() - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); + int to_line = main->first_invalid_line.load(); + if (to_line) { + MutexLock lock(main->lines[to_line - 1].text_buf->get_mutex()); + total_height = main->lines[to_line - 1].offset.y + main->lines[to_line - 1].text_buf->get_size().y + main->lines[to_line - 1].text_buf->get_line_count() * get_theme_constant(SNAME("line_separation")); } return total_height; } int RichTextLabel::get_content_width() const { int total_width = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { + MutexLock lock(main->lines[i].text_buf->get_mutex()); total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x); } return total_width; @@ -4603,6 +4862,14 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); + ClassDB::bind_method(D_METHOD("is_ready"), &RichTextLabel::is_ready); + + ClassDB::bind_method(D_METHOD("set_threaded", "threaded"), &RichTextLabel::set_threaded); + ClassDB::bind_method(D_METHOD("is_threaded"), &RichTextLabel::is_threaded); + + ClassDB::bind_method(D_METHOD("set_progress_bar_delay", "delay_ms"), &RichTextLabel::set_progress_bar_delay); + ClassDB::bind_method(D_METHOD("get_progress_bar_delay"), &RichTextLabel::get_progress_bar_delay); + ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters); ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters); @@ -4643,6 +4910,9 @@ void RichTextLabel::_bind_methods() { // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay"), "set_progress_bar_delay", "get_progress_bar_delay"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); @@ -4676,6 +4946,8 @@ void RichTextLabel::_bind_methods() { ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); + ADD_SIGNAL(MethodInfo("finished")); + BIND_ENUM_CONSTANT(AUTOWRAP_OFF); BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY); BIND_ENUM_CONSTANT(AUTOWRAP_WORD); @@ -4727,15 +4999,19 @@ RichTextLabel::VisibleCharactersBehavior RichTextLabel::get_visible_characters_b void RichTextLabel::set_visible_characters_behavior(RichTextLabel::VisibleCharactersBehavior p_behavior) { if (visible_chars_behavior != p_behavior) { + _stop_thread(); + visible_chars_behavior = p_behavior; - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); update(); } } void RichTextLabel::set_visible_characters(int p_visible) { if (visible_characters != p_visible) { + _stop_thread(); + visible_characters = p_visible; if (p_visible == -1) { percent_visible = 1; @@ -4746,8 +5022,8 @@ void RichTextLabel::set_visible_characters(int p_visible) { } } if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) { - main->first_invalid_line = 0; //invalidate ALL - _validate_line_caches(main); + main->first_invalid_line.store(0); //invalidate ALL + _validate_line_caches(); } update(); } @@ -4759,7 +5035,9 @@ int RichTextLabel::get_visible_characters() const { int RichTextLabel::get_character_line(int p_char) { int line_count = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { + MutexLock lock(main->lines[i].text_buf->get_mutex()); if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) { for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) { Vector2i range = main->lines[i].text_buf->get_line_range(j); @@ -4777,7 +5055,8 @@ int RichTextLabel::get_character_line(int p_char) { int RichTextLabel::get_character_paragraph(int p_char) { int para_count = 0; - for (int i = 0; i < main->lines.size(); i++) { + int to_line = main->first_invalid_line.load(); + for (int i = 0; i < to_line; i++) { if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) { return para_count; } else { @@ -4802,7 +5081,6 @@ int RichTextLabel::get_total_character_count() const { } it = _get_next_item(it, true); } - return tc; } @@ -4812,7 +5090,8 @@ int RichTextLabel::get_total_glyph_count() const { while (it) { if (it->type == ITEM_FRAME) { ItemFrame *f = static_cast<ItemFrame *>(it); - for (int i = 0; i < f->lines.size(); i++) { + for (int i = 0; i < (int)f->lines.size(); i++) { + MutexLock lock(f->lines[i].text_buf->get_mutex()); tg += TS->shaped_text_get_glyph_count(f->lines[i].text_buf->get_rid()); } } @@ -4836,7 +5115,6 @@ Size2 RichTextLabel::get_minimum_size() const { } if (fit_content_height) { - const_cast<RichTextLabel *>(this)->_validate_line_caches(main); size.y += get_content_height(); } @@ -5034,10 +5312,10 @@ RichTextLabel::RichTextLabel(const String &p_text) { main->index = 0; current = main; main->lines.resize(1); - main->lines.write[0].from = main; - main->first_invalid_line = 0; - main->first_resized_line = 0; - main->first_invalid_font_line = 0; + main->lines[0].from = main; + main->first_invalid_line.store(0); + main->first_resized_line.store(0); + main->first_invalid_font_line.store(0); current_frame = main; vscroll = memnew(VScrollBar); @@ -5052,10 +5330,13 @@ RichTextLabel::RichTextLabel(const String &p_text) { vscroll->hide(); set_text(p_text); + updating.store(false); + stop_thread.store(false); set_clip_contents(true); } RichTextLabel::~RichTextLabel() { + _stop_thread(); memdelete(main); } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index c6d0d0875d..7fbd5f1745 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -141,10 +141,10 @@ private: struct ItemFrame : public Item { bool cell = false; - Vector<Line> lines; - int first_invalid_line = 0; - int first_invalid_font_line = 0; - int first_resized_line = 0; + LocalVector<Line> lines; + std::atomic<int> first_invalid_line; + std::atomic<int> first_invalid_font_line; + std::atomic<int> first_resized_line; ItemFrame *parent_frame = nullptr; @@ -155,7 +155,12 @@ private: Size2 max_size_over = Size2(-1, -1); Rect2 padding; - ItemFrame() { type = ITEM_FRAME; } + ItemFrame() { + type = ITEM_FRAME; + first_invalid_line.store(0); + first_invalid_font_line.store(0); + first_resized_line.store(0); + } }; struct ItemText : public Item { @@ -263,8 +268,8 @@ private: int width = 0; }; - Vector<Column> columns; - Vector<float> rows; + LocalVector<Column> columns; + LocalVector<float> rows; int total_width = 0; int total_height = 0; @@ -363,6 +368,16 @@ private: Item *current = nullptr; ItemFrame *current_frame = nullptr; + Thread thread; + Mutex data_mutex; + bool threaded = false; + std::atomic<bool> stop_thread; + std::atomic<bool> updating; + std::atomic<double> loaded; + + uint64_t loading_started = 0; + int progress_delay = 1000; + VScrollBar *vscroll = nullptr; AutowrapMode autowrap_mode = AUTOWRAP_WORD_SMART; @@ -392,7 +407,11 @@ private: Array custom_effects; void _invalidate_current_line(ItemFrame *p_frame); - void _validate_line_caches(ItemFrame *p_frame); + + static void _thread_function(void *self); + void _stop_thread(); + bool _validate_line_caches(); + void _process_line_caches(); void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false); void _remove_item(Item *p_item, const int p_line, const int p_subitem_line); @@ -446,8 +465,9 @@ private: bool _search_line(ItemFrame *p_frame, int p_line, const String &p_string, int p_char_idx, bool p_reverse_search); bool _search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search); - void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); - void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width); + float _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h, int *r_char_offset); + float _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, float p_h); + void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size); int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs); float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false); @@ -480,9 +500,9 @@ private: bool _find_layout_subitem(Item *from, Item *to); void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); - void _update_scroll(); void _update_fx(ItemFrame *p_frame, double p_delta_time); void _scroll_changed(double); + int _find_first_line(int p_from, int p_to, int p_vofs) const; virtual void gui_input(const Ref<InputEvent> &p_event) override; virtual String get_tooltip(const Point2 &p_pos) const override; @@ -611,6 +631,14 @@ public: bool is_deselect_on_focus_loss_enabled() const; void deselect(); + bool is_ready() const; + + void set_threaded(bool p_threaded); + bool is_threaded() const; + + void set_progress_bar_delay(int p_delay_ms); + int get_progress_bar_delay() const; + // Context menu. PopupMenu *get_menu() const; bool is_menu_visible() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 315ffbd419..050c510a96 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -4756,9 +4756,7 @@ void TextEdit::add_gutter(int p_at) { gutters.insert(p_at, GutterInfo()); } - for (int i = 0; i < text.size() + 1; i++) { - text.add_gutter(p_at); - } + text.add_gutter(p_at); emit_signal(SNAME("gutter_added")); update(); } @@ -4768,9 +4766,7 @@ void TextEdit::remove_gutter(int p_gutter) { gutters.remove_at(p_gutter); - for (int i = 0; i < text.size() + 1; i++) { - text.remove_gutter(p_gutter); - } + text.remove_gutter(p_gutter); emit_signal(SNAME("gutter_removed")); update(); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 194cad72d1..993203bee6 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -356,7 +356,7 @@ private: Vector<int> last_visible_chars; }; - Map<int, LineDrawingCache> line_drawing_cache; + HashMap<int, LineDrawingCache> line_drawing_cache; int _get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const; @@ -511,7 +511,6 @@ private: /* Syntax highlighting. */ Ref<SyntaxHighlighter> syntax_highlighter; - Map<int, Dictionary> syntax_highlighting_cache; Dictionary _get_line_syntax_highlighting(int p_line); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 8961b5ba54..bd791dff2a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1637,7 +1637,7 @@ Node *Node::find_common_parent_with(const Node *p_node) const { return const_cast<Node *>(p_node); } - Set<const Node *> visited; + RBSet<const Node *> visited; const Node *n = this; @@ -1669,7 +1669,7 @@ NodePath Node::get_path_to(const Node *p_node) const { return NodePath("."); } - Set<const Node *> visited; + RBSet<const Node *> visited; const Node *n = this; @@ -1763,15 +1763,15 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) { void Node::remove_from_group(const StringName &p_identifier) { ERR_FAIL_COND(!data.grouped.has(p_identifier)); - Map<StringName, GroupData>::Element *E = data.grouped.find(p_identifier); + HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier); ERR_FAIL_COND(!E); if (data.tree) { - data.tree->remove_from_group(E->key(), this); + data.tree->remove_from_group(E->key, this); } - data.grouped.erase(E); + data.grouped.remove(E); } Array Node::_get_groups() const { @@ -2042,7 +2042,7 @@ StringName Node::get_property_store_alias(const StringName &p_property) const { } #endif -void Node::get_storable_properties(Set<StringName> &r_storable_properties) const { +void Node::get_storable_properties(RBSet<StringName> &r_storable_properties) const { List<PropertyInfo> pi; get_property_list(&pi); for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { @@ -2088,7 +2088,7 @@ bool Node::get_scene_instance_load_placeholder() const { return data.use_placeholder; } -Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const { +Node *Node::_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap) const { Node *node = nullptr; bool instantiated = false; @@ -2280,11 +2280,11 @@ Node *Node::duplicate(int p_flags) const { } #ifdef TOOLS_ENABLED -Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const { - return duplicate_from_editor(r_duplimap, Map<Ref<Resource>, Ref<Resource>>()); +Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const { + return duplicate_from_editor(r_duplimap, HashMap<Ref<Resource>, Ref<Resource>>()); } -Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { +Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. @@ -2300,7 +2300,7 @@ Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const M return dupe; } -void Node::remap_node_resources(Node *p_node, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { +void Node::remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { List<PropertyInfo> props; p_node->get_property_list(&props); @@ -2326,7 +2326,7 @@ void Node::remap_node_resources(Node *p_node, const Map<Ref<Resource>, Ref<Resou } } -void Node::remap_nested_resources(Ref<Resource> p_resource, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { +void Node::remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const { List<PropertyInfo> props; p_resource->get_property_list(&props); @@ -2528,9 +2528,10 @@ Node *Node::get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, int j = 0; // If not p_last_is_property, we shouldn't consider the last one as part of the resource for (; j < p_path.get_subname_count() - (int)p_last_is_property; j++) { - Variant new_res_v = j == 0 ? node->get(p_path.get_subname(j)) : r_res->get(p_path.get_subname(j)); + bool is_valid = false; + Variant new_res_v = j == 0 ? node->get(p_path.get_subname(j), &is_valid) : r_res->get(p_path.get_subname(j), &is_valid); - if (new_res_v.get_type() == Variant::NIL) { // Found nothing on that path + if (!is_valid) { // Found nothing on that path return nullptr; } diff --git a/scene/main/node.h b/scene/main/node.h index 72f340bbc3..8505d2618f 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -32,7 +32,7 @@ #define NODE_H #include "core/string/node_path.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "core/variant/typed_array.h" #include "scene/main/scene_tree.h" @@ -119,7 +119,7 @@ private: Viewport *viewport = nullptr; - Map<StringName, GroupData> grouped; + HashMap<StringName, GroupData> grouped; List<Node *>::Element *OW = nullptr; // Owned element. List<Node *> owned; @@ -177,7 +177,7 @@ private: Array _get_node_and_resource(const NodePath &p_path); void _duplicate_signals(const Node *p_original, Node *p_copy) const; - Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = nullptr) const; + Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const; TypedArray<Node> _get_children(bool p_include_internal = true) const; Array _get_groups() const; @@ -378,7 +378,7 @@ public: bool is_property_pinned(const StringName &p_property) const; virtual StringName get_property_store_alias(const StringName &p_property) const; #endif - void get_storable_properties(Set<StringName> &r_storable_properties) const; + void get_storable_properties(RBSet<StringName> &r_storable_properties) const; virtual String to_string() override; @@ -420,10 +420,10 @@ public: Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const; #ifdef TOOLS_ENABLED - Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const; - Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; - void remap_node_resources(Node *p_node, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; - void remap_nested_resources(Ref<Resource> p_resource, const Map<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; + Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const; + Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; + void remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; + void remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const; #endif // used by editors, to save what has changed only @@ -522,6 +522,6 @@ public: VARIANT_ENUM_CAST(Node::DuplicateFlags); -typedef Set<Node *, Node::Comparator> NodeSet; +typedef RBSet<Node *, Node::Comparator> NodeSet; #endif diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index 8fb7456335..71e62fe804 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -55,16 +55,16 @@ Array ResourcePreloader::_get_resources() const { arr.resize(resources.size()); names.resize(resources.size()); - Set<String> sorted_names; + RBSet<String> sorted_names; for (const KeyValue<StringName, Ref<Resource>> &E : resources) { sorted_names.insert(E.key); } int i = 0; - for (Set<String>::Element *E = sorted_names.front(); E; E = E->next()) { - names.set(i, E->get()); - arr[i] = resources[E->get()]; + for (const String &E : sorted_names) { + names.set(i, E); + arr[i] = resources[E]; i++; } @@ -123,8 +123,9 @@ Vector<String> ResourcePreloader::_get_resource_list() const { Vector<String> res; res.resize(resources.size()); int i = 0; - for (Map<StringName, Ref<Resource>>::Element *E = resources.front(); E; E = E->next(), i++) { - res.set(i, E->key()); + for (const KeyValue<StringName, Ref<Resource>> &E : resources) { + res.set(i, E.key); + i++; } return res; diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h index 2df8b5cda7..fe59bc8ae3 100644 --- a/scene/main/resource_preloader.h +++ b/scene/main/resource_preloader.h @@ -36,7 +36,7 @@ class ResourcePreloader : public Node { GDCLASS(ResourcePreloader, Node); - Map<StringName, Ref<Resource>> resources; + HashMap<StringName, Ref<Resource>> resources; void _set_resources(const Array &p_data); Array _get_resources() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index baa0362f63..231b672f63 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -129,32 +129,32 @@ void SceneTree::node_renamed(Node *p_node) { } SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { E = group_map.insert(p_group, Group()); } - ERR_FAIL_COND_V_MSG(E->get().nodes.has(p_node), &E->get(), "Already in group: " + p_group + "."); - E->get().nodes.push_back(p_node); - //E->get().last_tree_version=0; - E->get().changed = true; - return &E->get(); + ERR_FAIL_COND_V_MSG(E->value.nodes.has(p_node), &E->value, "Already in group: " + p_group + "."); + E->value.nodes.push_back(p_node); + //E->value.last_tree_version=0; + E->value.changed = true; + return &E->value; } void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); ERR_FAIL_COND(!E); - E->get().nodes.erase(p_node); - if (E->get().nodes.is_empty()) { - group_map.erase(E); + E->value.nodes.erase(p_node); + if (E->value.nodes.is_empty()) { + group_map.remove(E); } } void SceneTree::make_group_changed(const StringName &p_group) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (E) { - E->get().changed = true; + E->value.changed = true; } } @@ -173,17 +173,17 @@ void SceneTree::_flush_ugc() { ugc_locked = true; while (unique_group_calls.size()) { - Map<UGCall, Vector<Variant>>::Element *E = unique_group_calls.front(); + HashMap<UGCall, Vector<Variant>, UGCall>::Iterator E = unique_group_calls.begin(); - const Variant **argptrs = (const Variant **)alloca(E->get().size() * sizeof(Variant *)); + const Variant **argptrs = (const Variant **)alloca(E->value.size() * sizeof(Variant *)); - for (int i = 0; i < E->get().size(); i++) { - argptrs[i] = &E->get()[i]; + for (int i = 0; i < E->value.size(); i++) { + argptrs[i] = &E->value[i]; } - call_group_flagsp(GROUP_CALL_DEFAULT, E->key().group, E->key().call, argptrs, E->get().size()); + call_group_flagsp(GROUP_CALL_DEFAULT, E->key.group, E->key.call, argptrs, E->value.size()); - unique_group_calls.erase(E); + unique_group_calls.remove(E); } ugc_locked = false; @@ -211,11 +211,11 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) { } void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - Group &g = E->get(); + Group &g = E->value; if (g.nodes.is_empty()) { return; } @@ -284,11 +284,11 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro } void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - Group &g = E->get(); + Group &g = E->value; if (g.nodes.is_empty()) { return; } @@ -335,11 +335,11 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr } void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - Group &g = E->get(); + Group &g = E->value; if (g.nodes.is_empty()) { return; } @@ -818,11 +818,11 @@ bool SceneTree::is_paused() const { } void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - Group &g = E->get(); + Group &g = E->value; if (g.nodes.is_empty()) { return; } @@ -862,11 +862,11 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio } void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - Group &g = E->get(); + Group &g = E->value; if (g.nodes.is_empty()) { return; } @@ -952,20 +952,20 @@ int64_t SceneTree::get_frame() const { Array SceneTree::_get_nodes_in_group(const StringName &p_group) { Array ret; - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return ret; } - _update_group_order(E->get()); //update order just in case - int nc = E->get().nodes.size(); + _update_group_order(E->value); //update order just in case + int nc = E->value.nodes.size(); if (nc == 0) { return ret; } ret.resize(nc); - Node **ptr = E->get().nodes.ptrw(); + Node **ptr = E->value.nodes.ptrw(); for (int i = 0; i < nc; i++) { ret[i] = ptr[i]; } @@ -978,32 +978,32 @@ bool SceneTree::has_group(const StringName &p_identifier) const { } Node *SceneTree::get_first_node_in_group(const StringName &p_group) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return nullptr; // No group. } - _update_group_order(E->get()); // Update order just in case. + _update_group_order(E->value); // Update order just in case. - if (E->get().nodes.is_empty()) { + if (E->value.nodes.is_empty()) { return nullptr; } - return E->get().nodes[0]; + return E->value.nodes[0]; } void SceneTree::get_nodes_in_group(const StringName &p_group, List<Node *> *p_list) { - Map<StringName, Group>::Element *E = group_map.find(p_group); + HashMap<StringName, Group>::Iterator E = group_map.find(p_group); if (!E) { return; } - _update_group_order(E->get()); //update order just in case - int nc = E->get().nodes.size(); + _update_group_order(E->value); //update order just in case + int nc = E->value.nodes.size(); if (nc == 0) { return; } - Node **ptr = E->get().nodes.ptrw(); + Node **ptr = E->value.nodes.ptrw(); for (int i = 0; i < nc; i++) { p_list->push_back(ptr[i]); } diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index d633fb38d0..bdcfd2d35a 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -102,7 +102,7 @@ private: bool paused = false; int root_lock = 0; - Map<StringName, Group> group_map; + HashMap<StringName, Group> group_map; bool _quit = false; bool initialized = false; @@ -121,16 +121,20 @@ private: StringName group; StringName call; + static uint32_t hash(const UGCall &p_val) { + return p_val.group.hash() ^ p_val.call.hash(); + } + bool operator==(const UGCall &p_with) const { return group == p_with.group && call == p_with.call; } bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; } }; // Safety for when a node is deleted while a group is being called. int call_lock = 0; - Set<Node *> call_skip; // Skip erased nodes. + RBSet<Node *> call_skip; // Skip erased nodes. List<ObjectID> delete_queue; - Map<UGCall, Vector<Variant>> unique_group_calls; + HashMap<UGCall, Vector<Variant>, UGCall> unique_group_calls; bool ugc_locked = false; void _flush_ugc(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index e4037c2843..590c73de0b 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -412,9 +412,9 @@ void Viewport::_notification(int p_what) { #ifndef _3D_DISABLED if (audio_listener_3d_set.size() && !audio_listener_3d) { AudioListener3D *first = nullptr; - for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { - if (first == nullptr || first->is_greater_than(E->get())) { - first = E->get(); + for (AudioListener3D *E : audio_listener_3d_set) { + if (first == nullptr || first->is_greater_than(E)) { + first = E; } } @@ -426,9 +426,9 @@ void Viewport::_notification(int p_what) { if (camera_3d_set.size() && !camera_3d) { // There are cameras but no current camera, pick first in tree and make it current. Camera3D *first = nullptr; - for (Set<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) { - if (first == nullptr || first->is_greater_than(E->get())) { - first = E->get(); + for (Camera3D *E : camera_3d_set) { + if (first == nullptr || first->is_greater_than(E)) { + first = E; } } @@ -647,13 +647,13 @@ void Viewport::_process_picking() { uint64_t frame = get_tree()->get_frame(); PhysicsDirectSpaceState2D::ShapeResult res[64]; - for (Set<CanvasLayer *>::Element *E = canvas_layers.front(); E; E = E->next()) { + for (const CanvasLayer *E : canvas_layers) { Transform2D canvas_transform; ObjectID canvas_layer_id; - if (E->get()) { + if (E) { // A descendant CanvasLayer. - canvas_transform = E->get()->get_transform(); - canvas_layer_id = E->get()->get_instance_id(); + canvas_transform = E->get_transform(); + canvas_layer_id = E->get_instance_id(); } else { // This Viewport's builtin canvas. canvas_transform = get_canvas_transform(); @@ -675,23 +675,23 @@ void Viewport::_process_picking() { if (co && co->can_process()) { bool send_event = true; if (is_mouse) { - Map<ObjectID, uint64_t>::Element *F = physics_2d_mouseover.find(res[i].collider_id); + HashMap<ObjectID, uint64_t>::Iterator F = physics_2d_mouseover.find(res[i].collider_id); if (!F) { physics_2d_mouseover.insert(res[i].collider_id, frame); co->_mouse_enter(); } else { - F->get() = frame; + F->value = frame; // It was already hovered, so don't send the event if it's faked. if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) { send_event = false; } } - Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape)); + HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>>::Iterator SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape)); if (!SF) { physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame); co->_mouse_shape_enter(res[i].shape); } else { - SF->get() = frame; + SF->value = frame; } } @@ -1277,21 +1277,19 @@ void Viewport::_gui_show_tooltip() { gui.tooltip_popup->child_controls_changed(); } -void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) { +bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) { + bool stopped = false; Ref<InputEvent> ev = p_input; - // Mouse wheel events can't be stopped. - Ref<InputEventMouseButton> mb = p_input; + // Returns true if an event should be impacted by a control's mouse filter. + bool is_mouse_event = Ref<InputEventMouse>(p_input).is_valid(); - bool cant_stop_me_now = (mb.is_valid() && + Ref<InputEventMouseButton> mb = p_input; + bool is_scroll_event = mb.is_valid() && (mb->get_button_index() == MouseButton::WHEEL_DOWN || mb->get_button_index() == MouseButton::WHEEL_UP || mb->get_button_index() == MouseButton::WHEEL_LEFT || - mb->get_button_index() == MouseButton::WHEEL_RIGHT)); - Ref<InputEventPanGesture> pn = p_input; - cant_stop_me_now = pn.is_valid() || cant_stop_me_now; - - bool ismouse = ev.is_valid() || Object::cast_to<InputEventMouseMotion>(*p_input) != nullptr; + mb->get_button_index() == MouseButton::WHEEL_RIGHT); CanvasItem *ci = p_control; while (ci) { @@ -1305,9 +1303,12 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu break; } if (gui.key_event_accepted) { + stopped = true; break; } - if (!cant_stop_me_now && control->data.mouse_filter == Control::MOUSE_FILTER_STOP && ismouse) { + if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP && is_mouse_event && !(is_scroll_event && control->data.force_pass_scroll_events)) { + // Mouse events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true + stopped = true; break; } } @@ -1319,6 +1320,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu ev = ev->xformed_by(ci->get_transform()); // Transform event upwards. ci = ci->get_parent_item(); } + return stopped; } void Viewport::_gui_call_notification(Control *p_control, int p_what) { @@ -1530,11 +1532,14 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } + bool stopped = false; if (gui.mouse_focus && gui.mouse_focus->can_process()) { - _gui_call_input(gui.mouse_focus, mb); + stopped = _gui_call_input(gui.mouse_focus, mb); } - set_input_as_handled(); + if (stopped) { + set_input_as_handled(); + } if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) { // Alternate drop use (when using force_drag(), as proposed by #5342). @@ -1600,11 +1605,14 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.forced_mouse_focus = false; } + bool stopped = false; if (mouse_focus && mouse_focus->can_process()) { - _gui_call_input(mouse_focus, mb); + stopped = _gui_call_input(mouse_focus, mb); } - set_input_as_handled(); + if (stopped) { + set_input_as_handled(); + } } } @@ -1767,11 +1775,14 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape; + bool stopped = false; if (over && over->can_process()) { - _gui_call_input(over, mm); + stopped = _gui_call_input(over, mm); } - set_input_as_handled(); + if (stopped) { + set_input_as_handled(); + } } if (gui.drag_data.get_type() != Variant::NIL) { @@ -1884,6 +1895,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (touch_event->is_pressed()) { Control *over = gui_find_control(pos); if (over) { + bool stopped = false; if (over->can_process()) { touch_event = touch_event->xformed_by(Transform2D()); // Make a copy. if (over == gui.mouse_focus) { @@ -1892,19 +1904,24 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); } touch_event->set_position(pos); - _gui_call_input(over, touch_event); + stopped = _gui_call_input(over, touch_event); + } + if (stopped) { + set_input_as_handled(); } - set_input_as_handled(); return; } } else if (touch_event->get_index() == 0 && gui.last_mouse_focus) { + bool stopped = false; if (gui.last_mouse_focus->can_process()) { touch_event = touch_event->xformed_by(Transform2D()); // Make a copy. touch_event->set_position(gui.focus_inv_xform.xform(pos)); - _gui_call_input(gui.last_mouse_focus, touch_event); + stopped = _gui_call_input(gui.last_mouse_focus, touch_event); + } + if (stopped) { + set_input_as_handled(); } - set_input_as_handled(); return; } } @@ -1919,6 +1936,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Control *over = gui_find_control(pos); if (over) { + bool stopped = false; if (over->can_process()) { gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy. if (over == gui.mouse_focus) { @@ -1927,9 +1945,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); } gesture_event->set_position(pos); - _gui_call_input(over, gesture_event); + stopped = _gui_call_input(over, gesture_event); + } + if (stopped) { + set_input_as_handled(); } - set_input_as_handled(); return; } } @@ -1941,6 +1961,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { over = gui_find_control(drag_event->get_position()); } if (over) { + bool stopped = false; if (over->can_process()) { Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse(); Size2 pos = localizer.xform(drag_event->get_position()); @@ -1953,10 +1974,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { drag_event->set_relative(rel); drag_event->set_position(pos); - _gui_call_input(over, drag_event); + stopped = _gui_call_input(over, drag_event); } - set_input_as_handled(); + if (stopped) { + set_input_as_handled(); + } return; } } @@ -2250,14 +2273,14 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) { } void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) { - List<Map<ObjectID, uint64_t>::Element *> to_erase; + List<ObjectID> to_erase; - for (Map<ObjectID, uint64_t>::Element *E = physics_2d_mouseover.front(); E; E = E->next()) { - if (!p_clean_all_frames && E->get() == p_frame_reference) { + for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) { + if (!p_clean_all_frames && E.value == p_frame_reference) { continue; } - Object *o = ObjectDB::get_instance(E->key()); + Object *o = ObjectDB::get_instance(E.key); if (o) { CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o); if (co && co->is_inside_tree()) { @@ -2267,7 +2290,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus co->_mouse_exit(); } } - to_erase.push_back(E); + to_erase.push_back(E.key); } while (to_erase.size()) { @@ -2276,24 +2299,24 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus } // Per-shape. - List<Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *> shapes_to_erase; + List<Pair<ObjectID, int>> shapes_to_erase; - for (Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>>::Element *E = physics_2d_shape_mouseover.front(); E; E = E->next()) { - if (!p_clean_all_frames && E->get() == p_frame_reference) { + for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) { + if (!p_clean_all_frames && E.value == p_frame_reference) { continue; } - Object *o = ObjectDB::get_instance(E->key().first); + Object *o = ObjectDB::get_instance(E.key.first); if (o) { CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o); if (co && co->is_inside_tree()) { if (p_clean_all_frames && p_paused_only && co->can_process()) { continue; } - co->_mouse_shape_exit(E->key().second); + co->_mouse_shape_exit(E.key.second); } } - shapes_to_erase.push_back(E); + shapes_to_erase.push_back(E.key); } while (shapes_to_erase.size()) { @@ -3179,18 +3202,18 @@ void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) { void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) { if (audio_listener_3d_set.size() > 0) { - for (Set<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { - if (p_exclude == E->get()) { + for (AudioListener3D *E : audio_listener_3d_set) { + if (p_exclude == E) { continue; } - if (!E->get()->is_inside_tree()) { + if (!E->is_inside_tree()) { continue; } if (audio_listener_3d != nullptr) { return; } - E->get()->make_current(); + E->make_current(); } } else { // Attempt to reset listener to the camera position. @@ -3267,18 +3290,18 @@ void Viewport::_camera_3d_remove(Camera3D *p_camera) { } void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) { - for (Set<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) { - if (p_exclude == E->get()) { + for (Camera3D *E : camera_3d_set) { + if (p_exclude == E) { continue; } - if (!E->get()->is_inside_tree()) { + if (!E->is_inside_tree()) { continue; } if (camera_3d != nullptr) { return; } - E->get()->make_current(); + E->make_current(); } } @@ -3913,8 +3936,8 @@ Viewport::Viewport() { Viewport::~Viewport() { // Erase itself from viewport textures. - for (Set<ViewportTexture *>::Element *E = viewport_textures.front(); E; E = E->next()) { - E->get()->vp = nullptr; + for (ViewportTexture *E : viewport_textures) { + E->vp = nullptr; } RenderingServer::get_singleton()->free(viewport); } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index c1e71c69a3..48e4b175b6 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -203,7 +203,7 @@ private: AudioListener2D *audio_listener_2d = nullptr; Camera2D *camera_2d = nullptr; - Set<CanvasLayer *> canvas_layers; + RBSet<CanvasLayer *> canvas_layers; RID viewport; RID current_canvas; @@ -258,9 +258,9 @@ private: bool local_input_handled = false; // Collider to frame - Map<ObjectID, uint64_t> physics_2d_mouseover; + HashMap<ObjectID, uint64_t> physics_2d_mouseover; // Collider & shape to frame - Map<Pair<ObjectID, int>, uint64_t, PairSort<ObjectID, int>> physics_2d_shape_mouseover; + HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>> physics_2d_shape_mouseover; // Cleans up colliders corresponding to old frames or all of them. void _cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference = 0); @@ -301,7 +301,7 @@ private: bool use_occlusion_culling = false; Ref<ViewportTexture> default_texture; - Set<ViewportTexture *> viewport_textures; + RBSet<ViewportTexture *> viewport_textures; SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT; SDFScale sdf_scale = SDF_SCALE_50_PERCENT; @@ -381,7 +381,7 @@ private: bool disable_input = false; - void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); + bool _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input); void _gui_call_notification(Control *p_control, int p_what); void _gui_sort_roots(); @@ -615,7 +615,7 @@ public: bool use_xr = false; friend class AudioListener3D; AudioListener3D *audio_listener_3d = nullptr; - Set<AudioListener3D *> audio_listener_3d_set; + RBSet<AudioListener3D *> audio_listener_3d_set; bool is_audio_listener_3d_enabled = false; RID internal_audio_listener_3d; AudioListener3D *get_audio_listener_3d() const; @@ -650,7 +650,7 @@ public: friend class Camera3D; Camera3D *camera_3d = nullptr; - Set<Camera3D *> camera_3d_set; + RBSet<Camera3D *> camera_3d_set; Camera3D *get_camera_3d() const; void _camera_3d_transform_changed_notify(); void _camera_3d_set(Camera3D *p_camera); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 6feccb7eec..193f18c075 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -263,9 +263,9 @@ void Window::_make_window() { DisplayServer::get_singleton()->window_set_transient(window_id, transient_parent->window_id); } - for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) { - if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) { - DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, transient_parent->window_id); + for (const Window *E : transient_children) { + if (E->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(E->window_id, transient_parent->window_id); } } @@ -290,9 +290,9 @@ void Window::_clear_window() { DisplayServer::get_singleton()->window_set_transient(window_id, DisplayServer::INVALID_WINDOW_ID); } - for (Set<Window *>::Element *E = transient_children.front(); E; E = E->next()) { - if (E->get()->window_id != DisplayServer::INVALID_WINDOW_ID) { - DisplayServer::get_singleton()->window_set_transient(E->get()->window_id, DisplayServer::INVALID_WINDOW_ID); + for (const Window *E : transient_children) { + if (E->window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_transient(E->window_id, DisplayServer::INVALID_WINDOW_ID); } } diff --git a/scene/main/window.h b/scene/main/window.h index f674f6425a..80dd9a854c 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -131,7 +131,7 @@ private: void _make_transient(); Window *transient_parent = nullptr; Window *exclusive_child = nullptr; - Set<Window *> transient_children; + RBSet<Window *> transient_children; friend class Control; Ref<Theme> theme; diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h index 63948e39a5..ac35df7ff3 100644 --- a/scene/multiplayer/multiplayer_spawner.h +++ b/scene/multiplayer/multiplayer_spawner.h @@ -47,7 +47,7 @@ public: private: TypedArray<PackedScene> spawnable_scenes; - Set<ResourceUID::ID> spawnable_ids; + RBSet<ResourceUID::ID> spawnable_ids; NodePath spawn_path; struct SpawnInfo { diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp index a933758946..7c271341db 100644 --- a/scene/multiplayer/scene_cache_interface.cpp +++ b/scene/multiplayer/scene_cache_interface.cpp @@ -132,9 +132,9 @@ void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_pack PathSentCache *psc = path_send_cache.getptr(path); ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from); + HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from); ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); - E->get() = true; + E->value = true; } Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) { @@ -182,9 +182,9 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) { const PathSentCache *psc = path_send_cache.getptr(p_path); ERR_FAIL_COND_V(!psc, false); - const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer); + HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer); ERR_FAIL_COND_V(!F, false); // Should never happen. - return F->get(); + return F->value; } bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) { @@ -205,28 +205,28 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int if (p_peer_id > 0) { // Fast single peer check. - Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer_id); + HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id); if (!F) { peers_to_add.push_back(p_peer_id); // Need to also be notified. has_all_peers = false; - } else if (!F->get()) { + } else if (!F->value) { has_all_peers = false; } } else { // Long and painful. - for (const Set<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) { - if (p_peer_id < 0 && E->get() == -p_peer_id) { + for (const int &E : multiplayer->get_connected_peers()) { + if (p_peer_id < 0 && E == -p_peer_id) { continue; // Continue, excluded. } - if (p_peer_id > 0 && E->get() != p_peer_id) { + if (p_peer_id > 0 && E != p_peer_id) { continue; // Continue, not for this peer. } - Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); + HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E); if (!F) { - peers_to_add.push_back(E->get()); // Need to also be notified. + peers_to_add.push_back(E); // Need to also be notified. has_all_peers = false; - } else if (!F->get()) { + } else if (!F->value) { has_all_peers = false; } } @@ -242,13 +242,13 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) { Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); ERR_FAIL_COND_V(!root_node, nullptr); - Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); + HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from); ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); - Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_cache_id); + HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id); ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from)); - PathGetCache::NodeInfo *ni = &F->get(); + PathGetCache::NodeInfo *ni = &F->value; Node *node = root_node->get_node(ni->path); if (!node) { ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h index d53cf387fb..3116233b5b 100644 --- a/scene/multiplayer/scene_cache_interface.h +++ b/scene/multiplayer/scene_cache_interface.h @@ -41,7 +41,7 @@ private: //path sent caches struct PathSentCache { - Map<int, bool> confirmed_peers; + HashMap<int, bool> confirmed_peers; int id; }; @@ -52,11 +52,11 @@ private: ObjectID instance; }; - Map<int, NodeInfo> nodes; + HashMap<int, NodeInfo> nodes; }; HashMap<NodePath, PathSentCache> path_send_cache; - Map<int, PathGetCache> path_get_cache; + HashMap<int, PathGetCache> path_get_cache; int last_send_cache_id = 1; protected: diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp index 63259bcd39..55266c53ad 100644 --- a/scene/multiplayer/scene_replication_interface.cpp +++ b/scene/multiplayer/scene_replication_interface.cpp @@ -316,7 +316,7 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p } void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) { - const Set<ObjectID> &known = rep_state->get_known_nodes(p_peer); + const RBSet<ObjectID> &known = rep_state->get_known_nodes(p_peer); if (known.is_empty()) { return; } diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp index 9a9b08b67b..e5cc57ff31 100644 --- a/scene/multiplayer/scene_replication_state.cpp +++ b/scene/multiplayer/scene_replication_state.cpp @@ -93,8 +93,8 @@ bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_ms return false; } -const Set<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), Set<ObjectID>()); +const RBSet<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) { + ERR_FAIL_COND_V(!peers_info.has(p_peer), RBSet<ObjectID>()); return peers_info[p_peer].known_nodes; } diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h index 6ac9265b67..33f72363ac 100644 --- a/scene/multiplayer/scene_replication_state.h +++ b/scene/multiplayer/scene_replication_state.h @@ -62,27 +62,27 @@ private: }; struct PeerInfo { - Set<ObjectID> known_nodes; + RBSet<ObjectID> known_nodes; HashMap<uint32_t, ObjectID> recv_nodes; uint16_t last_sent_sync = 0; uint16_t last_recv_sync = 0; }; - Set<int> known_peers; + RBSet<int> known_peers; uint32_t last_net_id = 0; HashMap<ObjectID, TrackedNode> tracked_nodes; HashMap<int, PeerInfo> peers_info; - Set<ObjectID> spawned_nodes; - Set<ObjectID> path_only_nodes; + RBSet<ObjectID> spawned_nodes; + RBSet<ObjectID> path_only_nodes; TrackedNode &_track(const ObjectID &p_id); void _untrack(const ObjectID &p_id); bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); } public: - const Set<int> get_peers() const { return known_peers; } - const Set<ObjectID> &get_spawned_nodes() const { return spawned_nodes; } - const Set<ObjectID> &get_path_only_nodes() const { return path_only_nodes; } + const RBSet<int> get_peers() const { return known_peers; } + const RBSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; } + const RBSet<ObjectID> &get_path_only_nodes() const { return path_only_nodes; } MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; } MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; } @@ -90,7 +90,7 @@ public: bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time); bool update_sync_time(const ObjectID &p_id, uint64_t p_msec); - const Set<ObjectID> get_known_nodes(int p_peer); + const RBSet<ObjectID> get_known_nodes(int p_peer); uint32_t get_net_id(const ObjectID &p_id) const; void set_net_id(const ObjectID &p_id, uint32_t p_net_id); uint32_t ensure_net_id(const ObjectID &p_id); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 632952c2cb..f70d57291f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -769,6 +769,7 @@ void register_scene_types() { GDREGISTER_CLASS(PrismMesh); GDREGISTER_CLASS(QuadMesh); GDREGISTER_CLASS(SphereMesh); + GDREGISTER_CLASS(TextMesh); GDREGISTER_CLASS(TubeTrailMesh); GDREGISTER_CLASS(RibbonTrailMesh); GDREGISTER_CLASS(PointMesh); @@ -1076,15 +1077,15 @@ void register_scene_types() { OS::get_singleton()->yield(); // may take time to init for (int i = 0; i < 20; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i + 1), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/2d_render"), i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/3d_render"), i + 1), ""); } for (int i = 0; i < 32; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i + 1), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i + 1), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i + 1), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/2d_physics"), i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/2d_navigation"), i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/3d_physics"), i + 1), ""); + GLOBAL_DEF_BASIC(vformat("%s/layer_%d", PNAME("layer_names/3d_navigation"), i + 1), ""); } if (RenderingServer::get_singleton()) { diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index 21f0162eb3..7a69cd140a 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -43,7 +43,7 @@ class AnimationLibrary : public Resource { TypedArray<StringName> _get_animation_list() const; friend class AnimationPlayer; //for faster access - Map<StringName, Ref<Animation>> animations; + HashMap<StringName, Ref<Animation>> animations; protected: static void _bind_methods(); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index c2988c2e8c..634fb3ef2f 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -170,8 +170,8 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) int curx = startx; int cury = starty; unsigned int count = 0; - Set<Point2i> case9s; - Set<Point2i> case6s; + RBSet<Point2i> case9s; + RBSet<Point2i> case6s; Vector<Vector2> _points; do { int sv = 0; diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp index 2d668cdf7f..aa6cc4aded 100644 --- a/scene/resources/canvas_item_material.cpp +++ b/scene/resources/canvas_item_material.cpp @@ -34,7 +34,7 @@ Mutex CanvasItemMaterial::material_mutex; SelfList<CanvasItemMaterial>::List *CanvasItemMaterial::dirty_materials = nullptr; -Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map; +HashMap<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData, CanvasItemMaterial::MaterialKey> CanvasItemMaterial::shader_map; CanvasItemMaterial::ShaderNames *CanvasItemMaterial::shader_names = nullptr; void CanvasItemMaterial::init_shaders() { diff --git a/scene/resources/canvas_item_material.h b/scene/resources/canvas_item_material.h index b097d174f0..7c44c125a8 100644 --- a/scene/resources/canvas_item_material.h +++ b/scene/resources/canvas_item_material.h @@ -63,8 +63,11 @@ private: uint32_t key = 0; - bool operator<(const MaterialKey &p_key) const { - return key < p_key.key; + static uint32_t hash(const MaterialKey &p_key) { + return hash_djb2_one_32(p_key.key); + } + bool operator==(const MaterialKey &p_key) const { + return key == p_key.key; } }; @@ -81,7 +84,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index 3e178108c4..081271c2fc 100644 --- a/scene/resources/concave_polygon_shape_3d.cpp +++ b/scene/resources/concave_polygon_shape_3d.cpp @@ -33,7 +33,7 @@ #include "servers/physics_server_3d.h" Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { - Set<DrawEdge> edges; + RBSet<DrawEdge> edges; int index_count = faces.size(); ERR_FAIL_COND_V((index_count % 3) != 0, Vector<Vector3>()); @@ -50,9 +50,9 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { Vector<Vector3> points; points.resize(edges.size() * 2); int idx = 0; - for (Set<DrawEdge>::Element *E = edges.front(); E; E = E->next()) { - points.write[idx + 0] = E->get().a; - points.write[idx + 1] = E->get().b; + for (const DrawEdge &E : edges) { + points.write[idx + 0] = E.a; + points.write[idx + 1] = E.b; idx += 2; } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index c719455a0a..c99f71b13e 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -49,6 +49,18 @@ const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed"; Curve::Curve() { } +void Curve::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (_points.size() >= p_count) { + _points.resize(p_count); + mark_dirty(); + } else { + for (int i = p_count - _points.size(); i > 0; i--) { + add_point(Vector2()); + } + } +} + int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) { // Add a point and preserve order @@ -358,6 +370,7 @@ real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) cons void Curve::mark_dirty() { _baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); + notify_property_list_changed(); } Array Curve::get_data() const { @@ -409,7 +422,6 @@ void Curve::set_data(const Array p_input) { p.position = p_input[i]; p.left_tangent = p_input[i + 1]; p.right_tangent = p_input[i + 2]; - // TODO For some reason the compiler won't convert from Variant to enum int left_mode = p_input[i + 3]; int right_mode = p_input[i + 4]; p.left_mode = (TangentMode)left_mode; @@ -490,8 +502,91 @@ void Curve::ensure_default_setup(real_t p_min, real_t p_max) { } } +bool Curve::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + Vector2 position = p_value.operator Vector2(); + set_point_offset(point_index, position.x); + set_point_value(point_index, position.y); + return true; + } else if (property == "left_tangent") { + set_point_left_tangent(point_index, p_value); + return true; + } else if (property == "left_mode") { + int mode = p_value; + set_point_left_mode(point_index, (TangentMode)mode); + return true; + } else if (property == "right_tangent") { + set_point_right_tangent(point_index, p_value); + return true; + } else if (property == "right_mode") { + int mode = p_value; + set_point_right_mode(point_index, (TangentMode)mode); + return true; + } + } + return false; +} + +bool Curve::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "left_tangent") { + r_ret = get_point_left_tangent(point_index); + return true; + } else if (property == "left_mode") { + r_ret = get_point_left_mode(point_index); + return true; + } else if (property == "right_tangent") { + r_ret = get_point_right_tangent(point_index); + return true; + } else if (property == "right_mode") { + r_ret = get_point_right_mode(point_index); + return true; + } + } + return false; +} + +void Curve::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < _points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/left_tangent", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("point_%d/left_mode", i), PROPERTY_HINT_ENUM, "Free,Linear"); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != _points.size() - 1) { + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/right_tangent", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + pi = PropertyInfo(Variant::INT, vformat("point_%d/right_mode", i), PROPERTY_HINT_ENUM, "Free,Linear"); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + } +} + void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE)); ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points); @@ -523,6 +618,7 @@ void Curve::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value"); ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED)); @@ -535,6 +631,20 @@ int Curve2D::get_point_count() const { return points.size(); } +void Curve2D::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (points.size() >= p_count) { + points.resize(p_count); + mark_dirty(); + baked_cache_dirty = true; + emit_signal(CoreStringNames::get_singleton()->changed); + } else { + for (int i = p_count - points.size(); i > 0; i--) { + add_point(Vector2()); + } + } +} + void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) { Point n; n.position = p_position; @@ -546,16 +656,14 @@ void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Ve points.push_back(n); } - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } void Curve2D::set_point_position(int p_index, const Vector2 &p_position) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].position = p_position; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_position(int p_index) const { @@ -567,8 +675,7 @@ void Curve2D::set_point_in(int p_index, const Vector2 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].in = p_in; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_in(int p_index) const { @@ -580,8 +687,7 @@ void Curve2D::set_point_out(int p_index, const Vector2 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].out = p_out; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector2 Curve2D::get_point_out(int p_index) const { @@ -592,15 +698,13 @@ Vector2 Curve2D::get_point_out(int p_index) const { void Curve2D::remove_point(int p_index) { ERR_FAIL_INDEX(p_index, points.size()); points.remove_at(p_index); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } void Curve2D::clear_points() { if (!points.is_empty()) { points.clear(); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } } @@ -632,7 +736,13 @@ Vector2 Curve2D::interpolatef(real_t p_findex) const { return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } -void Curve2D::_bake_segment2d(Map<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { +void Curve2D::mark_dirty() { + baked_cache_dirty = true; + emit_signal(CoreStringNames::get_singleton()->changed); + notify_property_list_changed(); +} + +void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { real_t mp = p_begin + (p_end - p_begin) * 0.5; Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b); Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b); @@ -825,8 +935,7 @@ PackedVector2Array Curve2D::get_baked_points() const { void Curve2D::set_bake_interval(real_t p_tolerance) { bake_interval = p_tolerance; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve2D::get_bake_interval() const { @@ -954,7 +1063,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con if (points.size() == 0) { return tess; } - Vector<Map<real_t, Vector2>> midpoints; + + // The current implementation requires a sorted map. + Vector<RBMap<real_t, Vector2>> midpoints; midpoints.resize(points.size() - 1); @@ -983,8 +1094,67 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +bool Curve2D::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + set_point_position(point_index, p_value); + return true; + } else if (property == "in") { + set_point_in(point_index, p_value); + return true; + } else if (property == "out") { + set_point_out(point_index, p_value); + return true; + } + } + return false; +} + +bool Curve2D::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "in") { + r_ret = get_point_in(point_index); + return true; + } else if (property == "out") { + r_ret = get_point_out(point_index); + return true; + } + } + return false; +} + +void Curve2D::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/in", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != points.size() - 1) { + pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/out", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + } +} + void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve2D::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position); @@ -1012,6 +1182,7 @@ void Curve2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); } Curve2D::Curve2D() {} @@ -1027,6 +1198,18 @@ int Curve3D::get_point_count() const { return points.size(); } +void Curve3D::set_point_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (points.size() >= p_count) { + points.resize(p_count); + mark_dirty(); + } else { + for (int i = p_count - points.size(); i > 0; i--) { + add_point(Vector3()); + } + } +} + void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) { Point n; n.position = p_position; @@ -1038,16 +1221,14 @@ void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Ve points.push_back(n); } - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } void Curve3D::set_point_position(int p_index, const Vector3 &p_position) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].position = p_position; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_position(int p_index) const { @@ -1059,8 +1240,7 @@ void Curve3D::set_point_tilt(int p_index, real_t p_tilt) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].tilt = p_tilt; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve3D::get_point_tilt(int p_index) const { @@ -1072,8 +1252,7 @@ void Curve3D::set_point_in(int p_index, const Vector3 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].in = p_in; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_in(int p_index) const { @@ -1085,8 +1264,7 @@ void Curve3D::set_point_out(int p_index, const Vector3 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); points.write[p_index].out = p_out; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } Vector3 Curve3D::get_point_out(int p_index) const { @@ -1097,15 +1275,13 @@ Vector3 Curve3D::get_point_out(int p_index) const { void Curve3D::remove_point(int p_index) { ERR_FAIL_INDEX(p_index, points.size()); points.remove_at(p_index); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } void Curve3D::clear_points() { if (!points.is_empty()) { points.clear(); - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } } @@ -1137,7 +1313,13 @@ Vector3 Curve3D::interpolatef(real_t p_findex) const { return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } -void Curve3D::_bake_segment3d(Map<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { +void Curve3D::mark_dirty() { + baked_cache_dirty = true; + emit_signal(CoreStringNames::get_singleton()->changed); + notify_property_list_changed(); +} + +void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const { real_t mp = p_begin + (p_end - p_begin) * 0.5; Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b); Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b); @@ -1599,8 +1781,7 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { void Curve3D::set_bake_interval(real_t p_tolerance) { bake_interval = p_tolerance; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } real_t Curve3D::get_bake_interval() const { @@ -1609,8 +1790,7 @@ real_t Curve3D::get_bake_interval() const { void Curve3D::set_up_vector_enabled(bool p_enable) { up_vector_enabled = p_enable; - baked_cache_dirty = true; - emit_signal(CoreStringNames::get_singleton()->changed); + mark_dirty(); } bool Curve3D::is_up_vector_enabled() const { @@ -1668,7 +1848,7 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con if (points.size() == 0) { return tess; } - Vector<Map<real_t, Vector3>> midpoints; + Vector<RBMap<real_t, Vector3>> midpoints; midpoints.resize(points.size() - 1); @@ -1697,8 +1877,77 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +bool Curve3D::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + set_point_position(point_index, p_value); + return true; + } else if (property == "in") { + set_point_in(point_index, p_value); + return true; + } else if (property == "out") { + set_point_out(point_index, p_value); + return true; + } else if (property == "tilt") { + set_point_tilt(point_index, p_value); + return true; + } + } + return false; +} + +bool Curve3D::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> components = String(p_name).split("/", true, 2); + if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { + int point_index = components[0].trim_prefix("point_").to_int(); + String property = components[1]; + if (property == "position") { + r_ret = get_point_position(point_index); + return true; + } else if (property == "in") { + r_ret = get_point_in(point_index); + return true; + } else if (property == "out") { + r_ret = get_point_out(point_index); + return true; + } else if (property == "tilt") { + r_ret = get_point_tilt(point_index); + return true; + } + } + return false; +} + +void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const { + for (int i = 0; i < points.size(); i++) { + PropertyInfo pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/position", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + + if (i != 0) { + pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + if (i != points.size() - 1) { + pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } + + pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/tilt", i)); + pi.usage &= ~PROPERTY_USAGE_STORAGE; + p_list->push_back(pi); + } +} + void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count); + ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve3D::set_point_count); ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position); @@ -1733,6 +1982,7 @@ void Curve3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); + ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_"); ADD_GROUP("Up Vector", "up_vector_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled"); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 767900b843..834e7ffa07 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -76,6 +76,8 @@ public: int get_point_count() const { return _points.size(); } + void set_point_count(int p_count); + int add_point(Vector2 p_position, real_t left_tangent = 0, real_t right_tangent = 0, @@ -126,6 +128,10 @@ public: void ensure_default_setup(real_t p_min, real_t p_max); + 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; + protected: static void _bind_methods(); @@ -164,19 +170,26 @@ class Curve2D : public Resource { mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; + void mark_dirty(); + void _bake() const; real_t bake_interval = 5.0; - void _bake_segment2d(Map<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); + 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; + protected: static void _bind_methods(); public: int get_point_count() const; + void set_point_count(int p_count); void add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1); void set_point_position(int p_index, const Vector2 &p_position); Vector2 get_point_position(int p_index) const; @@ -228,20 +241,27 @@ class Curve3D : public Resource { mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; + void mark_dirty(); + void _bake() const; real_t bake_interval = 0.2; bool up_vector_enabled = true; - void _bake_segment3d(Map<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); + 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; + protected: static void _bind_methods(); public: int get_point_count() const; + void set_point_count(int p_count); void add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1); void set_point_position(int p_index, const Vector3 &p_position); Vector3 get_point_position(int p_index) const; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 271cf61171..4fb44446d9 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -686,6 +686,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const graphnode_breakpoint->set_border_color(Color(0.9, 0.29, 0.3)); Ref<StyleBoxFlat> graphnode_position = make_flat_stylebox(style_pressed_color, 18, 42, 18, 12, 6, true, 4); graphnode_position->set_border_color(Color(0.98, 0.89, 0.27)); + Ref<StyleBoxEmpty> graphnode_slot = make_empty_stylebox(0, 0, 0, 0); theme->set_stylebox("frame", "GraphNode", graphnode_normal); theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected); @@ -693,6 +694,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("comment_focus", "GraphNode", graphnode_comment_selected); theme->set_stylebox("breakpoint", "GraphNode", graphnode_breakpoint); theme->set_stylebox("position", "GraphNode", graphnode_position); + theme->set_stylebox("slot", "GraphNode", graphnode_slot); theme->set_icon("port", "GraphNode", icons["graph_port"]); theme->set_icon("close", "GraphNode", icons["close"]); @@ -704,6 +706,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("separation", "GraphNode", 2 * scale); theme->set_constant("title_offset", "GraphNode", 26 * scale); theme->set_constant("close_offset", "GraphNode", 22 * scale); + theme->set_constant("close_h_offset", "GraphNode", 22 * scale); theme->set_constant("port_offset", "GraphNode", 0); // Tree diff --git a/scene/resources/font.h b/scene/resources/font.h index 9a90032605..96bfe3678b 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -33,7 +33,7 @@ #include "core/io/resource.h" #include "core/templates/lru.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "scene/resources/texture.h" #include "servers/text_server.h" diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index cca875f708..0fbd29b1a6 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -306,7 +306,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle)); const Vector3 *normals_ptr = normals.ptr(); - Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices; + HashMap<Vector3, LocalVector<Pair<int, int>>> unique_vertices; LocalVector<int> vertex_remap; LocalVector<int> vertex_inverse_remap; @@ -320,10 +320,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli const Vector3 &v = vertices_ptr[j]; const Vector3 &n = normals_ptr[j]; - Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v); + HashMap<Vector3, LocalVector<Pair<int, int>>>::Iterator E = unique_vertices.find(v); if (E) { - const LocalVector<Pair<int, int>> &close_verts = E->get(); + const LocalVector<Pair<int, int>> &close_verts = E->value; bool found = false; for (unsigned int k = 0; k < close_verts.size(); k++) { @@ -706,15 +706,15 @@ void ImporterMesh::create_shadow_mesh() { Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX]; int vertex_count = vertices.size(); { - Map<Vector3, int> unique_vertices; + HashMap<Vector3, int> unique_vertices; const Vector3 *vptr = vertices.ptr(); for (int j = 0; j < vertex_count; j++) { const Vector3 &v = vptr[j]; - Map<Vector3, int>::Element *E = unique_vertices.find(v); + HashMap<Vector3, int>::Iterator E = unique_vertices.find(v); if (E) { - vertex_remap.push_back(E->get()); + vertex_remap.push_back(E->value); } else { int vcount = unique_vertices.size(); unique_vertices[v] = vcount; @@ -898,16 +898,16 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit Vector<uint32_t> indices; indices.resize(face_count * 3); { - Map<Vector3, uint32_t> vertex_map; + HashMap<Vector3, uint32_t> vertex_map; Vector3 *vertex_w = vertices.ptrw(); uint32_t *index_w = indices.ptrw(); for (int i = 0; i < face_count; i++) { for (int j = 0; j < 3; j++) { const Vector3 &vertex = faces[i].vertex[j]; - Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex); + HashMap<Vector3, uint32_t>::Iterator found_vertex = vertex_map.find(vertex); uint32_t index; if (found_vertex) { - index = found_vertex->get(); + index = found_vertex->value; } else { index = ++vertex_count; vertex_map[vertex] = index; @@ -960,7 +960,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() { return Ref<NavigationMesh>(); } - Map<Vector3, int> unique_vertices; + HashMap<Vector3, int> unique_vertices; LocalVector<int> face_indices; for (int i = 0; i < faces.size(); i++) { diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 27e1590940..b8171ca4bd 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -187,9 +187,9 @@ bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { } if (pr) { - const Map<StringName, Variant>::Element *E = param_cache.find(pr); + HashMap<StringName, Variant>::ConstIterator E = param_cache.find(pr); if (E) { - r_ret = E->get(); + r_ret = E->value; } else { r_ret = Variant(); } @@ -348,7 +348,7 @@ ShaderMaterial::~ShaderMaterial() { Mutex BaseMaterial3D::material_mutex; SelfList<BaseMaterial3D>::List *BaseMaterial3D::dirty_materials = nullptr; -Map<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData> BaseMaterial3D::shader_map; +HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map; BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr; void BaseMaterial3D::init_shaders() { @@ -1788,8 +1788,6 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { _validate_high_end("refraction", property); _validate_high_end("subsurf_scatter", property); - _validate_high_end("anisotropy", property); - _validate_high_end("clearcoat", property); _validate_high_end("heightmap", property); if (property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { diff --git a/scene/resources/material.h b/scene/resources/material.h index 7edb8b7317..b845fd68c8 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -82,7 +82,7 @@ class ShaderMaterial : public Material { GDCLASS(ShaderMaterial, Material); Ref<Shader> shader; - Map<StringName, Variant> param_cache; + HashMap<StringName, Variant> param_cache; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -323,6 +323,9 @@ private: memset(this, 0, sizeof(MaterialKey)); } + static uint32_t hash(const MaterialKey &p_key) { + return hash_djb2_buffer((const uint8_t *)&p_key, sizeof(MaterialKey)); + } bool operator==(const MaterialKey &p_key) const { return memcmp(this, &p_key, sizeof(MaterialKey)) == 0; } @@ -337,7 +340,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 253ba53c35..ab1873ffe9 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -455,7 +455,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { has_indices = true; } - Map<Vector3, Vector3> normal_accum; + HashMap<Vector3, Vector3> normal_accum; //fill normals with triangle normals for (int i = 0; i < vc; i += 3) { @@ -474,13 +474,13 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { Vector3 n = Plane(t[0], t[1], t[2]).normal; for (int j = 0; j < 3; j++) { - Map<Vector3, Vector3>::Element *E = normal_accum.find(t[j]); + HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t[j]); if (!E) { normal_accum[t[j]] = n; } else { - float d = n.dot(E->get()); + float d = n.dot(E->value); if (d < 1.0) { - E->get() += n * (1.0 - d); + E->value += n * (1.0 - d); } //E->get()+=n; } @@ -499,10 +499,10 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const { for (int i = 0; i < vc2; i++) { Vector3 t = r[i]; - Map<Vector3, Vector3>::Element *E = normal_accum.find(t); + HashMap<Vector3, Vector3>::Iterator E = normal_accum.find(t); ERR_CONTINUE(!E); - t += E->get() * p_margin; + t += E->value * p_margin; r[i] = t; } diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 594f723a1d..33d63adc71 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -150,7 +150,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf vertices.write[i] = v; } - Map<Point2i, int> edge_indices; + HashMap<Point2i, int> edge_indices; for (int i = 0; i < icount; i += 3) { Vertex *v[3] = { &vertices.write[r[i + 0]], &vertices.write[r[i + 1]], &vertices.write[r[i + 2]] }; diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 5168bf83eb..a90999bf07 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -100,14 +100,14 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { for (const KeyValue<int, Item> &E : item_map) { - String name = "item/" + itos(E.key) + "/"; - p_list->push_back(PropertyInfo(Variant::STRING, name + "name")); - p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform")); - p_list->push_back(PropertyInfo(Variant::ARRAY, name + "shapes")); - p_list->push_back(PropertyInfo(Variant::OBJECT, name + "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "navmesh_transform")); - p_list->push_back(PropertyInfo(Variant::OBJECT, name + "preview", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER)); + String name = vformat("%s/%d/", PNAME("item"), E.key); + p_list->push_back(PropertyInfo(Variant::STRING, name + PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + PNAME("mesh_transform"))); + p_list->push_back(PropertyInfo(Variant::ARRAY, name + PNAME("shapes"))); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("navmesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + PNAME("navmesh_transform"))); + p_list->push_back(PropertyInfo(Variant::OBJECT, name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_HELPER)); } } diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index e0f2ab2114..4105bd6960 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -32,7 +32,7 @@ #define MESH_LIBRARY_H #include "core/io/resource.h" -#include "core/templates/map.h" +#include "core/templates/rb_map.h" #include "mesh.h" #include "scene/3d/navigation_region_3d.h" #include "shape_3d.h" @@ -56,7 +56,7 @@ public: Ref<NavigationMesh> navmesh; }; - Map<int, Item> item_map; + RBMap<int, Item> item_map; void _set_item_shapes(int p_item, const Array &p_shapes); Array _get_item_shapes(int p_item) const; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 552fa84bad..dd0a8472a4 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -38,6 +38,7 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + WARN_PRINT("A mesh surface was skipped when creating a NavigationMesh due to wrong primitive type in the source mesh. Mesh surface must be made out of triangles."); continue; } Array arr = p_mesh->surface_get_arrays(i); @@ -46,6 +47,7 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX]; Vector<int> iarr = arr[Mesh::ARRAY_INDEX]; if (varr.size() == 0 || iarr.size() == 0) { + WARN_PRINT("A mesh surface was skipped when creating a NavigationMesh due to an empty vertex or index array."); continue; } @@ -229,7 +231,7 @@ float NavigationMesh::get_verts_per_poly() const { } void NavigationMesh::set_detail_sample_distance(float p_value) { - ERR_FAIL_COND(p_value < 0); + ERR_FAIL_COND(p_value < 0.1); detail_sample_distance = p_value; } @@ -338,7 +340,7 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { } } - Map<_EdgeKey, bool> edge_map; + HashMap<_EdgeKey, bool, _EdgeKey> edge_map; Vector<Vector3> tmeshfaces; tmeshfaces.resize(faces.size() * 3); @@ -356,10 +358,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { SWAP(ek.from, ek.to); } - Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); + HashMap<_EdgeKey, bool, _EdgeKey>::Iterator F = edge_map.find(ek); if (F) { - F->get() = false; + F->value = false; } else { edge_map[ek] = true; @@ -501,7 +503,7 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater"), "set_edge_max_length", "get_edge_max_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge/max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater"), "set_edge_max_error", "get_edge_max_error"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon/verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_distance", PROPERTY_HINT_RANGE, "0.1,16.0,0.01,or_greater"), "set_detail_sample_distance", "get_detail_sample_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail/sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater"), "set_detail_sample_max_error", "get_detail_sample_max_error"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter/low_hanging_obstacles"), "set_filter_low_hanging_obstacles", "get_filter_low_hanging_obstacles"); diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index e43e8627e4..4d8611c3c9 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -49,7 +49,13 @@ class NavigationMesh : public Resource { Vector3 from; Vector3 to; - bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } + static uint32_t hash(const _EdgeKey &p_key) { + return HashMapHasherDefault::hash(p_key.from) ^ HashMapHasherDefault::hash(p_key.to); + } + + bool operator==(const _EdgeKey &p_with) const { + return HashMapComparatorDefault<Vector3>::compare(from, p_with.from) && HashMapComparatorDefault<Vector3>::compare(to, p_with.to); + } }; protected: diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 12d1dc9925..c7b1981aed 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -53,7 +53,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { if (pinned.is_empty()) { return Array(); } - Set<StringName> storable_properties; + RBSet<StringName> storable_properties; p_node->get_storable_properties(storable_properties); int i = 0; do { @@ -106,7 +106,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty(); - Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene; + HashMap<Ref<Resource>, Ref<Resource>> resources_local_to_scene; for (int i = 0; i < nc; i++) { const NodeData &n = nd[i]; @@ -257,10 +257,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.find(res); + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); if (E) { - value = E->get(); + value = E->value; } else { Node *base = i == 0 ? node : ret_nodes[0]; @@ -430,7 +430,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { return ret_nodes[0]; } -static int _nm_get_string(const String &p_string, Map<StringName, int> &name_map) { +static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) { if (name_map.has(p_string)) { return name_map[p_string]; } @@ -450,7 +450,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia return idx; } -Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { +Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { // this function handles all the work related to properly packing scenes, be it // instantiated or inherited. // given the complexity of this process, an attempt will be made to properly @@ -672,7 +672,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } -Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { +Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { if (p_node != p_owner && p_node->get_owner() && p_node->get_owner() != p_owner && !p_owner->is_editable_instance(p_node->get_owner())) { return OK; } @@ -879,10 +879,10 @@ Error SceneState::pack(Node *p_scene) { Node *scene = p_scene; - Map<StringName, int> name_map; + HashMap<StringName, int> name_map; HashMap<Variant, int, VariantHasher, VariantComparator> variant_map; - Map<Node *, int> node_map; - Map<Node *, int> nodepath_map; + HashMap<Node *, int> node_map; + HashMap<Node *, int> nodepath_map; // If using scene inheritance, pack the scene it inherits from. if (scene->get_scene_inherited_state().is_valid()) { diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 96222937d0..05abb23284 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -42,7 +42,7 @@ class SceneState : public RefCounted { Vector<NodePath> node_paths; Vector<NodePath> editable_instances; mutable HashMap<NodePath, int> node_path_cache; - mutable Map<int, int> base_scene_node_remap; + mutable HashMap<int, int> base_scene_node_remap; int base_scene_idx = -1; @@ -83,8 +83,8 @@ class SceneState : public RefCounted { Vector<ConnectionData> connections; - Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map); - Error _parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map); + Error _parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map); + Error _parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map); String path; @@ -225,7 +225,9 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false) override; #ifdef TOOLS_ENABLED - virtual void set_last_modified_time(uint64_t p_time) override { state->set_last_modified_time(p_time); } + virtual void set_last_modified_time(uint64_t p_time) override { + state->set_last_modified_time(p_time); + } #endif Ref<SceneState> get_state() const; diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 597d070285..587b84a11d 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -34,7 +34,7 @@ Mutex ParticlesMaterial::material_mutex; SelfList<ParticlesMaterial>::List *ParticlesMaterial::dirty_materials = nullptr; -Map<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData> ParticlesMaterial::shader_map; +HashMap<ParticlesMaterial::MaterialKey, ParticlesMaterial::ShaderData, ParticlesMaterial::MaterialKey> ParticlesMaterial::shader_map; ParticlesMaterial::ShaderNames *ParticlesMaterial::shader_names = nullptr; void ParticlesMaterial::init_shaders() { diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 4c3a3ba16c..24341d964d 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -109,6 +109,14 @@ private: uint32_t key = 0; + static uint32_t hash(const MaterialKey &p_key) { + return hash_djb2_one_32(p_key.key); + } + + bool operator==(const MaterialKey &p_key) const { + return key == p_key.key; + } + bool operator<(const MaterialKey &p_key) const { return key < p_key.key; } @@ -119,7 +127,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 94e7f46ea5..6b0c22d720 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -34,8 +34,8 @@ bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { int crosses = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 a = points[e.points[0]].pos; Vector2 b = points[e.points[1]].pos; @@ -105,8 +105,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> bool valid = true; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == i || e.points[1] == i || e.points[0] == j || e.points[1] == j) { continue; } @@ -140,8 +140,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -151,7 +151,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float d = from.distance_squared_to(closest); if (d < closest_dist) { - ignore_from_edge = E->get(); + ignore_from_edge = E; closest_dist = d; closest_point = closest; } @@ -164,8 +164,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -175,7 +175,7 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float d = to.distance_squared_to(closest); if (d < closest_dist) { - ignore_to_edge = E->get(); + ignore_to_edge = E; closest_dist = d; closest_point = closest; } @@ -188,8 +188,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector { bool can_see_eachother = true; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == ignore_from_edge.points[0] && e.points[1] == ignore_from_edge.points[1]) { continue; } @@ -240,8 +240,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector valid_b = false; } - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; if (e.points[0] == i || e.points[1] == i) { continue; @@ -289,14 +289,14 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } //solve graph - Set<int> open_list; + RBSet<int> open_list; points.write[aidx].distance = 0; points.write[aidx].prev = aidx; - for (Set<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) { - open_list.insert(E->get()); - points.write[E->get()].distance = from.distance_to(points[E->get()].pos); - points.write[E->get()].prev = aidx; + for (const int &E : points[aidx].connections) { + open_list.insert(E); + points.write[E].distance = from.distance_to(points[E].pos); + points.write[E].prev = aidx; } bool found_route = false; @@ -312,14 +312,14 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector float least_cost = 1e30; //this could be faster (cache previous results) - for (Set<int>::Element *E = open_list.front(); E; E = E->next()) { - const Point &p = points[E->get()]; + for (const int &E : open_list) { + const Point &p = points[E]; float cost = p.distance; cost += p.pos.distance_to(to); cost += p.penalty; if (cost < least_cost) { - least_cost_point = E->get(); + least_cost_point = E; least_cost = cost; } } @@ -327,8 +327,8 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector const Point &np = points[least_cost_point]; //open the neighbours for search - for (Set<int>::Element *E = np.connections.front(); E; E = E->next()) { - Point &p = points.write[E->get()]; + for (const int &E : np.connections) { + Point &p = points.write[E]; float distance = np.pos.distance_to(p.pos) + np.distance; if (p.prev != -1) { @@ -343,9 +343,9 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector p.prev = least_cost_point; p.distance = distance; - open_list.insert(E->get()); + open_list.insert(E); - if (E->get() == bidx) { + if (E == bidx) { //oh my reached end! stop algorithm found_route = true; break; @@ -459,8 +459,8 @@ Dictionary PolygonPathFinder::_get_data() const { { int *cw = c.ptrw(); int idx = 0; - for (Set<int>::Element *E = points[i].connections.front(); E; E = E->next()) { - cw[idx++] = E->get(); + for (const int &E : points[i].connections) { + cw[idx++] = E; } } connections[i] = c; @@ -469,9 +469,9 @@ Dictionary PolygonPathFinder::_get_data() const { { int *iw = ind.ptrw(); int idx = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - iw[idx++] = E->get().points[0]; - iw[idx++] = E->get().points[1]; + for (const Edge &E : edges) { + iw[idx++] = E.points[0]; + iw[idx++] = E.points[1]; } } @@ -492,8 +492,8 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - const Edge &e = E->get(); + for (const Edge &E : edges) { + const Edge &e = E; Vector2 seg[2] = { points[e.points[0]].pos, points[e.points[1]].pos @@ -516,9 +516,9 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { Vector<Vector2> PolygonPathFinder::get_intersections(const Vector2 &p_from, const Vector2 &p_to) const { Vector<Vector2> inters; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { - Vector2 a = points[E->get().points[0]].pos; - Vector2 b = points[E->get().points[1]].pos; + for (const Edge &E : edges) { + Vector2 a = points[E.points[0]].pos; + Vector2 b = points[E.points[1]].pos; Vector2 res; if (Geometry2D::segment_intersects_segment(a, b, p_from, p_to, &res)) { diff --git a/scene/resources/polygon_path_finder.h b/scene/resources/polygon_path_finder.h index db96192917..71ad77eb6e 100644 --- a/scene/resources/polygon_path_finder.h +++ b/scene/resources/polygon_path_finder.h @@ -38,7 +38,7 @@ class PolygonPathFinder : public Resource { struct Point { Vector2 pos; - Set<int> connections; + RBSet<int> connections; float distance = 0.0; float penalty = 0.0; int prev = 0; @@ -68,7 +68,7 @@ class PolygonPathFinder : public Resource { Rect2 bounds; Vector<Point> points; - Set<Edge> edges; + RBSet<Edge> edges; bool _is_point_inside(const Vector2 &p_point) const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index c9a890194d..3009bdb449 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -29,7 +29,12 @@ /*************************************************************************/ #include "primitive_meshes.h" + +#include "core/core_string_names.h" +#include "scene/resources/theme.h" #include "servers/rendering_server.h" +#include "thirdparty/misc/clipper.hpp" +#include "thirdparty/misc/polypartition.h" /** PrimitiveMesh @@ -2151,3 +2156,819 @@ void RibbonTrailMesh::_bind_methods() { RibbonTrailMesh::RibbonTrailMesh() { } + +/*************************************************************************/ +/* TextMesh */ +/*************************************************************************/ + +void TextMesh::_generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_gl) const { + if (cache.has(p_hash)) { + return; + } + + GlyphMeshData &gl_data = cache[p_hash]; + + Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index); + Vector2 origin = Vector2(p_gl.x_off, p_gl.y_off) * pixel_size; + + PackedVector3Array points = d["points"]; + PackedInt32Array contours = d["contours"]; + bool orientation = d["orientation"]; + + if (points.size() < 3 || contours.size() < 1) { + return; // No full contours, only glyph control points (or nothing), ignore. + } + + // Approximate Bezier curves as polygons. + // See https://freetype.org/freetype2/docs/glyphs/glyphs-6.html, for more info. + for (int i = 0; i < contours.size(); i++) { + int32_t start = (i == 0) ? 0 : (contours[i - 1] + 1); + int32_t end = contours[i]; + Vector<ContourPoint> polygon; + + for (int32_t j = start; j <= end; j++) { + if (points[j].z == TextServer::CONTOUR_CURVE_TAG_ON) { + // Point on the curve. + Vector2 p = Vector2(points[j].x, points[j].y) * pixel_size + origin; + polygon.push_back(ContourPoint(p, true)); + } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + // Conic Bezier arc. + int32_t next = (j == end) ? start : (j + 1); + int32_t prev = (j == start) ? end : (j - 1); + Vector2 p0; + Vector2 p1 = Vector2(points[j].x, points[j].y); + Vector2 p2; + + // For successive conic OFF points add a virtual ON point in the middle. + if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + p0 = (Vector2(points[prev].x, points[prev].y) + Vector2(points[j].x, points[j].y)) / 2.0; + } else if (points[prev].z == TextServer::CONTOUR_CURVE_TAG_ON) { + p0 = Vector2(points[prev].x, points[prev].y); + } else { + ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j)); + } + if (points[next].z == TextServer::CONTOUR_CURVE_TAG_OFF_CONIC) { + p2 = (Vector2(points[j].x, points[j].y) + Vector2(points[next].x, points[next].y)) / 2.0; + } else if (points[next].z == TextServer::CONTOUR_CURVE_TAG_ON) { + p2 = Vector2(points[next].x, points[next].y); + } else { + ERR_FAIL_MSG(vformat("Invalid conic arc point sequence at %d:%d", i, j)); + } + + real_t step = CLAMP(curve_step / (p0 - p2).length(), 0.01, 0.5); + real_t t = step; + while (t < 1.0) { + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t t2 = t * t; + + Vector2 point = p1 + omt2 * (p0 - p1) + t2 * (p2 - p1); + Vector2 p = point * pixel_size + origin; + polygon.push_back(ContourPoint(p, false)); + t += step; + } + } else if (points[j].z == TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC) { + // Cubic Bezier arc. + int32_t next1 = (j == end) ? start : (j + 1); + int32_t next2 = (next1 == end) ? start : (next1 + 1); + int32_t prev = (j == start) ? end : (j - 1); + + // There must be exactly two OFF points and two ON points for each cubic arc. + ERR_FAIL_COND_MSG(points[prev].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, prev)); + ERR_FAIL_COND_MSG(points[next1].z != TextServer::CONTOUR_CURVE_TAG_OFF_CUBIC, vformat("Invalid cubic arc point sequence at %d:%d", i, next1)); + ERR_FAIL_COND_MSG(points[next2].z != TextServer::CONTOUR_CURVE_TAG_ON, vformat("Invalid cubic arc point sequence at %d:%d", i, next2)); + + Vector2 p0 = Vector2(points[prev].x, points[prev].y); + Vector2 p1 = Vector2(points[j].x, points[j].y); + Vector2 p2 = Vector2(points[next1].x, points[next1].y); + Vector2 p3 = Vector2(points[next2].x, points[next2].y); + + real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5); + real_t t = step; + while (t < 1.0) { + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = t * t; + real_t t3 = t2 * t; + + Vector2 point = p0 * omt3 + p1 * omt2 * t * 3.0 + p2 * omt * t2 * 3.0 + p3 * t3; + Vector2 p = point * pixel_size + origin; + polygon.push_back(ContourPoint(p, false)); + t += step; + } + i++; + } else { + ERR_FAIL_MSG(vformat("Unknown point tag at %d:%d", i, j)); + } + } + + if (polygon.size() < 3) { + continue; // Skip glyph control points. + } + + if (!orientation) { + polygon.reverse(); + } + + gl_data.contours.push_back(polygon); + } + + // Calculate bounds. + List<TPPLPoly> in_poly; + for (int i = 0; i < gl_data.contours.size(); i++) { + TPPLPoly inp; + inp.Init(gl_data.contours[i].size()); + real_t length = 0.0; + for (int j = 0; j < gl_data.contours[i].size(); j++) { + int next = (j + 1 == gl_data.contours[i].size()) ? 0 : (j + 1); + + gl_data.min_p.x = MIN(gl_data.min_p.x, gl_data.contours[i][j].point.x); + gl_data.min_p.y = MIN(gl_data.min_p.y, gl_data.contours[i][j].point.y); + gl_data.max_p.x = MAX(gl_data.max_p.x, gl_data.contours[i][j].point.x); + gl_data.max_p.y = MAX(gl_data.max_p.y, gl_data.contours[i][j].point.y); + length += (gl_data.contours[i][next].point - gl_data.contours[i][j].point).length(); + + inp.GetPoint(j) = gl_data.contours[i][j].point; + } + TPPLOrientation poly_orient = inp.GetOrientation(); + if (poly_orient == TPPL_ORIENTATION_CW) { + inp.SetHole(true); + } + in_poly.push_back(inp); + gl_data.contours_info.push_back(ContourInfo(length, poly_orient == TPPL_ORIENTATION_CCW)); + } + + TPPLPartition tpart; + + //Decompose and triangulate. + List<TPPLPoly> out_poly; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { + ERR_FAIL_MSG("Convex decomposing failed!"); + } + List<TPPLPoly> out_tris; + for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + if (tpart.Triangulate_OPT(&(I->get()), &out_tris) == 0) { + ERR_FAIL_MSG("Triangulation failed!"); + } + } + + for (List<TPPLPoly>::Element *I = out_tris.front(); I; I = I->next()) { + TPPLPoly &tp = I->get(); + ERR_FAIL_COND(tp.GetNumPoints() != 3); // Trianges only. + + for (int i = 0; i < 3; i++) { + gl_data.triangles.push_back(Vector2(tp.GetPoint(i).x, tp.GetPoint(i).y)); + } + } +} + +void TextMesh::_create_mesh_array(Array &p_arr) const { + Ref<Font> font = _get_font_or_default(); + ERR_FAIL_COND(font.is_null()); + + if (dirty_cache) { + cache.clear(); + dirty_cache = false; + } + + // Update text buffer. + if (dirty_text) { + TS->shaped_text_clear(text_rid); + TS->shaped_text_set_direction(text_rid, text_direction); + + String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, language); + + Array stt; + if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { + GDVIRTUAL_CALL(_structured_text_parser, st_args, text, stt); + } else { + stt = TS->parse_structured_text(st_parser, st_args, text); + } + TS->shaped_text_set_bidi_override(text_rid, stt); + + dirty_text = false; + dirty_font = false; + } else if (dirty_font) { + int spans = TS->shaped_get_span_count(text_rid); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features); + } + + dirty_font = false; + } + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + TS->shaped_text_fit_to_width(text_rid, width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } else { + TS->shaped_text_fit_to_width(text_rid, -1, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } + + Vector2 offset; + const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid); + int gl_size = TS->shaped_text_get_glyph_count(text_rid); + float line_width = TS->shaped_text_get_width(text_rid) * pixel_size; + + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + offset.x = 0.0; + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -line_width / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -line_width; + } break; + } + + bool has_depth = !Math::is_zero_approx(depth); + + // Generate glyph data, precalculate size of the arrays and mesh bounds for UV. + int64_t p_size = 0; + int64_t i_size = 0; + + Vector2 min_p = Vector2(INFINITY, INFINITY); + Vector2 max_p = Vector2(-INFINITY, -INFINITY); + + Vector2 offset_pre = offset; + for (int i = 0; i < gl_size; i++) { + if (glyphs[i].font_rid != RID()) { + uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id()); + hash = hash_djb2_one_32(glyphs[i].index, hash); + + _generate_glyph_mesh_data(hash, glyphs[i]); + GlyphMeshData &gl_data = cache[hash]; + + p_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); + i_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); + + if (has_depth) { + for (int j = 0; j < gl_data.contours.size(); j++) { + p_size += glyphs[i].repeat * gl_data.contours[j].size() * 4; + i_size += glyphs[i].repeat * gl_data.contours[j].size() * 6; + } + } + + for (int j = 0; j < glyphs[i].repeat; j++) { + min_p.x = MIN(gl_data.min_p.x + offset_pre.x, min_p.x); + min_p.y = MIN(gl_data.min_p.y + offset_pre.y, min_p.y); + max_p.x = MAX(gl_data.max_p.x + offset_pre.x, max_p.x); + max_p.y = MAX(gl_data.max_p.y + offset_pre.y, max_p.y); + + offset_pre.x += glyphs[i].advance * pixel_size; + } + } else { + p_size += glyphs[i].repeat * 4; + i_size += glyphs[i].repeat * 6; + + offset_pre.x += glyphs[i].advance * pixel_size * glyphs[i].repeat; + } + } + + Vector<Vector3> vertices; + Vector<Vector3> normals; + Vector<float> tangents; + Vector<Vector2> uvs; + Vector<int32_t> indices; + + vertices.resize(p_size); + normals.resize(p_size); + uvs.resize(p_size); + tangents.resize(p_size * 4); + indices.resize(i_size); + + Vector3 *vertices_ptr = vertices.ptrw(); + Vector3 *normals_ptr = normals.ptrw(); + float *tangents_ptr = tangents.ptrw(); + Vector2 *uvs_ptr = uvs.ptrw(); + int32_t *indices_ptr = indices.ptrw(); + + // Generate mesh. + int32_t p_idx = 0; + int32_t i_idx = 0; + for (int i = 0; i < gl_size; i++) { + if (glyphs[i].font_rid != RID()) { + uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id()); + hash = hash_djb2_one_32(glyphs[i].index, hash); + + const GlyphMeshData &gl_data = cache[hash]; + + int64_t ts = gl_data.triangles.size(); + const Vector2 *ts_ptr = gl_data.triangles.ptr(); + + for (int j = 0; j < glyphs[i].repeat; j++) { + for (int k = 0; k < ts; k += 3) { + // Add front face. + for (int l = 0; l < 3; l++) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, depth / 2.0); + vertices_ptr[p_idx] = point; + normals_ptr[p_idx] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); + } else { + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); + } + tangents_ptr[p_idx * 4 + 0] = 1.0; + tangents_ptr[p_idx * 4 + 1] = 0.0; + tangents_ptr[p_idx * 4 + 2] = 0.0; + tangents_ptr[p_idx * 4 + 3] = 1.0; + indices_ptr[i_idx++] = p_idx; + p_idx++; + } + if (has_depth) { + // Add back face. + for (int l = 2; l >= 0; l--) { + Vector3 point = Vector3(ts_ptr[k + l].x + offset.x, -ts_ptr[k + l].y + offset.y, -depth / 2.0); + vertices_ptr[p_idx] = point; + normals_ptr[p_idx] = Vector3(0.0, 0.0, -1.0); + uvs_ptr[p_idx] = Vector2(Math::range_lerp(point.x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(point.y, -min_p.y, -max_p.y, real_t(0.4), real_t(0.8))); + tangents_ptr[p_idx * 4 + 0] = -1.0; + tangents_ptr[p_idx * 4 + 1] = 0.0; + tangents_ptr[p_idx * 4 + 2] = 0.0; + tangents_ptr[p_idx * 4 + 3] = 1.0; + indices_ptr[i_idx++] = p_idx; + p_idx++; + } + } + } + // Add sides. + if (has_depth) { + for (int k = 0; k < gl_data.contours.size(); k++) { + int64_t ps = gl_data.contours[k].size(); + const ContourPoint *ps_ptr = gl_data.contours[k].ptr(); + const ContourInfo &ps_info = gl_data.contours_info[k]; + real_t length = 0.0; + for (int l = 0; l < ps; l++) { + int prev = (l == 0) ? (ps - 1) : (l - 1); + int next = (l + 1 == ps) ? 0 : (l + 1); + Vector2 d1; + Vector2 d2 = (ps_ptr[next].point - ps_ptr[l].point).normalized(); + if (ps_ptr[l].sharp) { + d1 = d2; + } else { + d1 = (ps_ptr[l].point - ps_ptr[prev].point).normalized(); + } + real_t seg_len = (ps_ptr[next].point - ps_ptr[l].point).length(); + + Vector3 quad_faces[4] = { + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, -depth / 2.0), + Vector3(ps_ptr[l].point.x + offset.x, -ps_ptr[l].point.y + offset.y, depth / 2.0), + Vector3(ps_ptr[next].point.x + offset.x, -ps_ptr[next].point.y + offset.y, depth / 2.0), + }; + for (int m = 0; m < 4; m++) { + const Vector2 &d = ((m % 2) == 0) ? d1 : d2; + real_t u_pos = ((m % 2) == 0) ? length : length + seg_len; + vertices_ptr[p_idx + m] = quad_faces[m]; + normals_ptr[p_idx + m] = Vector3(d.y, d.x, 0.0); + if (m < 2) { + uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.8 : 0.9); + } else { + uvs_ptr[p_idx + m] = Vector2(Math::range_lerp(u_pos, 0, ps_info.length, real_t(0.0), real_t(1.0)), (ps_info.ccw) ? 0.9 : 1.0); + } + tangents_ptr[(p_idx + m) * 4 + 0] = d.x; + tangents_ptr[(p_idx + m) * 4 + 1] = -d.y; + tangents_ptr[(p_idx + m) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + m) * 4 + 3] = 1.0; + } + + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; + + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 3; + indices_ptr[i_idx++] = p_idx + 2; + + length += seg_len; + p_idx += 4; + } + } + } + offset.x += glyphs[i].advance * pixel_size; + } + } else { + // Add fallback quad for missing glyphs. + for (int j = 0; j < glyphs[i].repeat; j++) { + Size2 sz = TS->get_hex_code_box_size(glyphs[i].font_size, glyphs[i].index) * pixel_size; + Vector3 quad_faces[4] = { + Vector3(offset.x, offset.y, 0.0), + Vector3(offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, sz.y + offset.y, 0.0), + Vector3(sz.x + offset.x, offset.y, 0.0), + }; + for (int k = 0; k < 4; k++) { + vertices_ptr[p_idx + k] = quad_faces[k]; + normals_ptr[p_idx + k] = Vector3(0.0, 0.0, 1.0); + if (has_depth) { + uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(0.4))); + } else { + uvs_ptr[p_idx + k] = Vector2(Math::range_lerp(quad_faces[k].x, min_p.x, max_p.x, real_t(0.0), real_t(1.0)), Math::range_lerp(quad_faces[k].y, -min_p.y, -max_p.y, real_t(0.0), real_t(1.0))); + } + tangents_ptr[(p_idx + k) * 4 + 0] = 1.0; + tangents_ptr[(p_idx + k) * 4 + 1] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 2] = 0.0; + tangents_ptr[(p_idx + k) * 4 + 3] = 1.0; + } + + indices_ptr[i_idx++] = p_idx; + indices_ptr[i_idx++] = p_idx + 1; + indices_ptr[i_idx++] = p_idx + 2; + + indices_ptr[i_idx++] = p_idx + 0; + indices_ptr[i_idx++] = p_idx + 2; + indices_ptr[i_idx++] = p_idx + 3; + p_idx += 4; + + offset.x += glyphs[i].advance * pixel_size; + } + } + } + + if (p_size == 0) { + // If empty, add single trinagle to suppress errors. + vertices.push_back(Vector3()); + normals.push_back(Vector3()); + uvs.push_back(Vector2()); + tangents.push_back(1.0); + tangents.push_back(0.0); + tangents.push_back(0.0); + tangents.push_back(1.0); + indices.push_back(0); + indices.push_back(0); + indices.push_back(0); + } + + p_arr[RS::ARRAY_VERTEX] = vertices; + p_arr[RS::ARRAY_NORMAL] = normals; + p_arr[RS::ARRAY_TANGENT] = tangents; + p_arr[RS::ARRAY_TEX_UV] = uvs; + p_arr[RS::ARRAY_INDEX] = indices; +} + +void TextMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &TextMesh::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &TextMesh::get_horizontal_alignment); + + ClassDB::bind_method(D_METHOD("set_text", "text"), &TextMesh::set_text); + ClassDB::bind_method(D_METHOD("get_text"), &TextMesh::get_text); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &TextMesh::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &TextMesh::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "font_size"), &TextMesh::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &TextMesh::get_font_size); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &TextMesh::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &TextMesh::get_depth); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &TextMesh::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &TextMesh::get_width); + + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &TextMesh::set_pixel_size); + ClassDB::bind_method(D_METHOD("get_pixel_size"), &TextMesh::get_pixel_size); + + ClassDB::bind_method(D_METHOD("set_curve_step", "curve_step"), &TextMesh::set_curve_step); + ClassDB::bind_method(D_METHOD("get_curve_step"), &TextMesh::get_curve_step); + + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction); + + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextMesh::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextMesh::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextMesh::clear_opentype_features); + + ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextMesh::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &TextMesh::get_structured_text_bidi_override); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &TextMesh::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &TextMesh::get_structured_text_bidi_override_options); + + ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &TextMesh::set_uppercase); + ClassDB::bind_method(D_METHOD("is_uppercase"), &TextMesh::is_uppercase); + + ClassDB::bind_method(D_METHOD("_font_changed"), &TextMesh::_font_changed); + ClassDB::bind_method(D_METHOD("_request_update"), &TextMesh::_request_update); + + ADD_GROUP("Text", ""); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); + + ADD_GROUP("Mesh", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_step", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "set_curve_step", "get_curve_step"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width"); + + ADD_GROUP("Locale", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); +} + +void TextMesh::_notification(int p_what) { + switch (p_what) { + case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = tr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + dirty_text = true; + _request_update(); + } break; + } +} + +bool TextMesh::_set(const StringName &p_name, const Variant &p_value) { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + int value = p_value; + if (value == -1) { + if (opentype_features.has(tag)) { + opentype_features.erase(tag); + dirty_font = true; + _request_update(); + } + } else { + if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { + opentype_features[tag] = value; + dirty_font = true; + _request_update(); + } + } + notify_property_list_changed(); + return true; + } + + return false; +} + +bool TextMesh::_get(const StringName &p_name, Variant &r_ret) const { + String str = p_name; + if (str.begins_with("opentype_features/")) { + String name = str.get_slicec('/', 1); + int32_t tag = TS->name_to_tag(name); + if (opentype_features.has(tag)) { + r_ret = opentype_features[tag]; + return true; + } else { + r_ret = -1; + return true; + } + } + return false; +} + +void TextMesh::_get_property_list(List<PropertyInfo> *p_list) const { + for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { + String name = TS->tag_to_name(*ftr); + p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); +} + +TextMesh::TextMesh() { + primitive_type = PRIMITIVE_TRIANGLES; + text_rid = TS->create_shaped_text(); +} + +TextMesh::~TextMesh() { + TS->free_rid(text_rid); +} + +void TextMesh::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (horizontal_alignment != p_alignment) { + horizontal_alignment = p_alignment; + _request_update(); + } +} + +HorizontalAlignment TextMesh::get_horizontal_alignment() const { + return horizontal_alignment; +} + +void TextMesh::set_text(const String &p_string) { + if (text != p_string) { + text = p_string; + xl_text = tr(text); + dirty_text = true; + _request_update(); + } +} + +String TextMesh::get_text() const { + return text; +} + +void TextMesh::_font_changed() { + dirty_font = true; + dirty_cache = true; + call_deferred(SNAME("_request_update")); +} + +void TextMesh::set_font(const Ref<Font> &p_font) { + if (font_override != p_font) { + if (font_override.is_valid()) { + font_override->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + font_override = p_font; + dirty_font = true; + dirty_cache = true; + if (font_override.is_valid()) { + font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + _request_update(); + } +} + +Ref<Font> TextMesh::get_font() const { + return font_override; +} + +Ref<Font> TextMesh::_get_font_or_default() const { + if (font_override.is_valid() && font_override->get_data_count() > 0) { + return font_override; + } + + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + } + } + } + + // If they don't exist, use any type to return the default/empty value. + return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); +} + +void TextMesh::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = CLAMP(p_size, 1, 127); + dirty_font = true; + dirty_cache = true; + _request_update(); + } +} + +int TextMesh::get_font_size() const { + return font_size; +} + +void TextMesh::set_depth(real_t p_depth) { + if (depth != p_depth) { + depth = MAX(p_depth, 0.0); + _request_update(); + } +} + +real_t TextMesh::get_depth() const { + return depth; +} + +void TextMesh::set_width(real_t p_width) { + if (width != p_width) { + width = p_width; + _request_update(); + } +} + +real_t TextMesh::get_width() const { + return width; +} + +void TextMesh::set_pixel_size(real_t p_amount) { + if (pixel_size != p_amount) { + pixel_size = CLAMP(p_amount, 0.0001, 128.0); + dirty_cache = true; + _request_update(); + } +} + +real_t TextMesh::get_pixel_size() const { + return pixel_size; +} + +void TextMesh::set_curve_step(real_t p_step) { + if (curve_step != p_step) { + curve_step = CLAMP(p_step, 0.1, 10.0); + dirty_cache = true; + _request_update(); + } +} + +real_t TextMesh::get_curve_step() const { + return curve_step; +} + +void TextMesh::set_text_direction(TextServer::Direction p_text_direction) { + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (text_direction != p_text_direction) { + text_direction = p_text_direction; + dirty_text = true; + _request_update(); + } +} + +TextServer::Direction TextMesh::get_text_direction() const { + return text_direction; +} + +void TextMesh::clear_opentype_features() { + opentype_features.clear(); + dirty_font = true; + _request_update(); +} + +void TextMesh::set_opentype_feature(const String &p_name, int p_value) { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { + opentype_features[tag] = p_value; + dirty_font = true; + _request_update(); + } +} + +int TextMesh::get_opentype_feature(const String &p_name) const { + int32_t tag = TS->name_to_tag(p_name); + if (!opentype_features.has(tag)) { + return -1; + } + return opentype_features[tag]; +} + +void TextMesh::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + dirty_text = true; + _request_update(); + } +} + +String TextMesh::get_language() const { + return language; +} + +void TextMesh::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + dirty_text = true; + _request_update(); + } +} + +TextServer::StructuredTextParser TextMesh::get_structured_text_bidi_override() const { + return st_parser; +} + +void TextMesh::set_structured_text_bidi_override_options(Array p_args) { + if (st_args != p_args) { + st_args = p_args; + dirty_text = true; + _request_update(); + } +} + +Array TextMesh::get_structured_text_bidi_override_options() const { + return st_args; +} + +void TextMesh::set_uppercase(bool p_uppercase) { + if (uppercase != p_uppercase) { + uppercase = p_uppercase; + dirty_text = true; + _request_update(); + } +} + +bool TextMesh::is_uppercase() const { + return uppercase; +} diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 6b0d5993a1..3849c92a7b 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -31,7 +31,9 @@ #ifndef PRIMITIVE_MESHES_H #define PRIMITIVE_MESHES_H +#include "scene/resources/font.h" #include "scene/resources/mesh.h" +#include "servers/text_server.h" ///@TODO probably should change a few integers to unsigned integers... @@ -447,5 +449,129 @@ public: RibbonTrailMesh(); }; +/** + Text... +*/ + +class TextMesh : public PrimitiveMesh { + GDCLASS(TextMesh, PrimitiveMesh); + +private: + struct ContourPoint { + Vector2 point; + bool sharp = false; + + ContourPoint(){}; + ContourPoint(const Vector2 &p_pt, bool p_sharp) { + point = p_pt; + sharp = p_sharp; + }; + }; + struct ContourInfo { + real_t length = 0.0; + bool ccw = true; + ContourInfo(){}; + ContourInfo(real_t p_len, bool p_ccw) { + length = p_len; + ccw = p_ccw; + } + }; + struct GlyphMeshData { + Vector<Vector2> triangles; + Vector<Vector<ContourPoint>> contours; + Vector<ContourInfo> contours_info; + Vector2 min_p = Vector2(INFINITY, INFINITY); + Vector2 max_p = Vector2(-INFINITY, -INFINITY); + }; + mutable HashMap<uint32_t, GlyphMeshData> cache; + + RID text_rid; + String text; + String xl_text; + + int font_size = 16; + Ref<Font> font_override; + float width = 500.0; + + HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; + bool uppercase = false; + Dictionary opentype_features; + String language; + TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; + Array st_args; + + real_t depth = 0.05; + real_t pixel_size = 0.01; + real_t curve_step = 0.5; + + mutable bool dirty_text = true; + mutable bool dirty_font = true; + mutable bool dirty_cache = true; + + void _generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_glyph) const; + void _font_changed(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + + virtual void _create_mesh_array(Array &p_arr) const override; + + 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; + +public: + GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + + TextMesh(); + ~TextMesh(); + + void set_horizontal_alignment(HorizontalAlignment p_alignment); + HorizontalAlignment get_horizontal_alignment() const; + + void set_text(const String &p_string); + String get_text() const; + + void set_font(const Ref<Font> &p_font); + Ref<Font> get_font() const; + Ref<Font> _get_font_or_default() const; + + void set_font_size(int p_size); + int get_font_size() const; + + void set_text_direction(TextServer::Direction p_text_direction); + TextServer::Direction get_text_direction() const; + + void set_opentype_feature(const String &p_name, int p_value); + int get_opentype_feature(const String &p_name) const; + void clear_opentype_features(); + + void set_language(const String &p_language); + String get_language() const; + + void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser); + TextServer::StructuredTextParser get_structured_text_bidi_override() const; + + void set_structured_text_bidi_override_options(Array p_args); + Array get_structured_text_bidi_override_options() const; + + void set_uppercase(bool p_uppercase); + bool is_uppercase() const; + + void set_width(real_t p_width); + real_t get_width() const; + + void set_depth(real_t p_depth); + real_t get_depth() const; + + void set_curve_step(real_t p_step); + real_t get_curve_step() const; + + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; +}; + VARIANT_ENUM_CAST(RibbonTrailMesh::Shape) #endif diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index ae321fd9a7..193bd0ac05 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -867,7 +867,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d } } -Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const Map<String, String> &p_map) { +Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map) { open(p_f, true); ERR_FAIL_COND_V(error != OK, error); ignore_resource_parsing = true; @@ -1232,7 +1232,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa } if (!assign.is_empty()) { - Map<StringName, int> empty_string_map; //unused + HashMap<StringName, int> empty_string_map; //unused bs_save_unicode_string(wf2, assign, true); ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); prop_count++; @@ -1293,7 +1293,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa String name = E.name; Variant value = packed_scene->get(name); - Map<StringName, int> empty_string_map; //unused + HashMap<StringName, int> empty_string_map; //unused bs_save_unicode_string(wf2, name, true); ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_index_map, dummy_read.external_resources, empty_string_map); prop_count++; @@ -1507,7 +1507,7 @@ void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<Strin loader.get_dependencies(f, p_dependencies, p_add_types); } -Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { +Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) { Error err = OK; { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); @@ -1737,7 +1737,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso #ifdef TOOLS_ENABLED // Keep order from cached ids. - Set<String> cached_ids_found; + RBSet<String> cached_ids_found; for (KeyValue<Ref<Resource>, String> &E : external_resources) { String cached_id = E.key->get_id_for_path(local_path); if (cached_id.is_empty() || cached_ids_found.has(cached_id)) { @@ -1809,7 +1809,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso f->store_line(String()); // Separate. } - Set<String> used_unique_ids; + RBSet<String> used_unique_ids; for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) { Ref<Resource> res = E->get(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index e67df72d7e..adab503599 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -58,8 +58,8 @@ class ResourceLoaderText { bool ignore_resource_parsing = false; - Map<String, ExtResource> ext_resources; - Map<String, Ref<Resource>> int_resources; + HashMap<String, ExtResource> ext_resources; + HashMap<String, Ref<Resource>> int_resources; int resources_total = 0; int resource_current = 0; @@ -76,7 +76,7 @@ class ResourceLoaderText { ResourceUID::ID res_uid = ResourceUID::INVALID_ID; - Map<String, String> remaps; + HashMap<String, String> remaps; static Error _parse_sub_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_sub_resource(p_stream, r_res, line, r_err_str); } static Error _parse_ext_resources(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return reinterpret_cast<ResourceLoaderText *>(p_self)->_parse_ext_resource(p_stream, r_res, line, r_err_str); } @@ -90,10 +90,10 @@ class ResourceLoaderText { }; struct DummyReadData { - Map<Ref<Resource>, int> external_resources; - Map<String, Ref<Resource>> rev_external_resources; - Map<Ref<Resource>, int> resource_index_map; - Map<String, Ref<Resource>> resource_map; + HashMap<Ref<Resource>, int> external_resources; + HashMap<String, Ref<Resource>> rev_external_resources; + HashMap<Ref<Resource>, int> resource_index_map; + HashMap<String, Ref<Resource>> resource_map; }; static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); } @@ -124,7 +124,7 @@ public: String recognize(Ref<FileAccess> p_f); ResourceUID::ID get_uid(Ref<FileAccess> p_f); void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types); - Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const Map<String, String> &p_map); + Error rename_dependencies(Ref<FileAccess> p_f, const String &p_path, const HashMap<String, String> &p_map); Error save_as_binary(Ref<FileAccess> p_f, const String &p_path); ResourceLoaderText(); @@ -140,7 +140,7 @@ public: virtual String get_resource_type(const String &p_path) const; virtual ResourceUID::ID get_resource_uid(const String &p_path) const; virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); - virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); + virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path); @@ -163,12 +163,12 @@ class ResourceFormatSaverTextInstance { bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; } }; - Map<NonPersistentKey, Ref<Resource>> non_persistent_map; + RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map; - Set<Ref<Resource>> resource_set; + RBSet<Ref<Resource>> resource_set; List<Ref<Resource>> saved_resources; - Map<Ref<Resource>, String> external_resources; - Map<Ref<Resource>, String> internal_resources; + HashMap<Ref<Resource>, String> external_resources; + HashMap<Ref<Resource>, String> internal_resources; struct ResourceSort { Ref<Resource> resource; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 6b1f89454f..d49157b1b8 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -100,7 +100,7 @@ RID Shader::get_rid() const { void Shader::set_default_texture_param(const StringName &p_param, const Ref<Texture2D> &p_texture, int p_index) { if (p_texture.is_valid()) { if (!default_textures.has(p_param)) { - default_textures[p_param] = Map<int, Ref<Texture2D>>(); + default_textures[p_param] = HashMap<int, Ref<Texture2D>>(); } default_textures[p_param][p_index] = p_texture; RS::get_singleton()->shader_set_default_texture_param(shader, p_param, p_texture->get_rid(), p_index); @@ -126,7 +126,7 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param, int } void Shader::get_default_texture_param_list(List<StringName> *r_textures) const { - for (const KeyValue<StringName, Map<int, Ref<Texture2D>>> &E : default_textures) { + for (const KeyValue<StringName, HashMap<int, Ref<Texture2D>>> &E : default_textures) { r_textures->push_back(E.key); } } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 3212dcd287..11c9f60ce8 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -58,8 +58,8 @@ private: // shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make // conversion fast and save memory. mutable bool params_cache_dirty = true; - mutable Map<StringName, StringName> params_cache; //map a shader param to a material param.. - Map<StringName, Map<int, Ref<Texture2D>>> default_textures; + mutable HashMap<StringName, StringName> params_cache; //map a shader param to a material param.. + HashMap<StringName, HashMap<int, Ref<Texture2D>>> default_textures; virtual void _update_shader() const; //used for visual shader protected: @@ -86,9 +86,9 @@ public: get_param_list(nullptr); } - const Map<StringName, StringName>::Element *E = params_cache.find(p_param); + const HashMap<StringName, StringName>::Iterator E = params_cache.find(p_param); if (E) { - return E->get(); + return E->value; } return StringName(); } diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp index 54ed71999c..1c04ba0cd4 100644 --- a/scene/resources/skin.cpp +++ b/scene/resources/skin.cpp @@ -130,11 +130,12 @@ bool Skin::_get(const StringName &p_name, Variant &r_ret) const { } void Skin::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("bind_count"), PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); for (int i = 0; i < get_bind_count(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "bind/" + itos(i) + "/name")); - p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, "bind/" + itos(i) + "/pose")); + const String prefix = vformat("%s/%d/", PNAME("bind"), i); + p_list->push_back(PropertyInfo(Variant::STRING_NAME, prefix + PNAME("name"))); + p_list->push_back(PropertyInfo(Variant::INT, prefix + PNAME("bone"), PROPERTY_HINT_RANGE, "0,16384,1,or_greater", get_bind_name(i) != StringName() ? PROPERTY_USAGE_NO_EDITOR : PROPERTY_USAGE_DEFAULT)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prefix + PNAME("pose"))); } } diff --git a/scene/resources/sky.cpp b/scene/resources/sky.cpp index 9cb6a16f5c..735134e27b 100644 --- a/scene/resources/sky.cpp +++ b/scene/resources/sky.cpp @@ -83,7 +83,7 @@ void Sky::_bind_methods() { ClassDB::bind_method(D_METHOD("get_material"), &Sky::get_material); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,PanoramaSkyMaterial,ProceduralSkyMaterial,PhysicalSkyMaterial"), "set_material", "get_material"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,HighQuality,HighQualityIncremental,RealTime"), "set_process_mode", "get_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Automatic,High-Quality,High-Quality Incremental,Real-Time"), "set_process_mode", "get_process_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "radiance_size", PROPERTY_HINT_ENUM, "32,64,128,256,512,1024,2048"), "set_radiance_size", "get_radiance_size"); BIND_ENUM_CONSTANT(RADIANCE_SIZE_32); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index ff5a85392c..ba21b9fd17 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -33,38 +33,38 @@ #include "scene/scene_string_names.h" void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - if (p_at_pos >= 0 && p_at_pos < E->get().frames.size()) { - E->get().frames.insert(p_at_pos, p_frame); + if (p_at_pos >= 0 && p_at_pos < E->value.frames.size()) { + E->value.frames.insert(p_at_pos, p_frame); } else { - E->get().frames.push_back(p_frame); + E->value.frames.push_back(p_frame); } emit_changed(); } int SpriteFrames::get_frame_count(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().frames.size(); + return E->value.frames.size(); } void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().frames.remove_at(p_idx); + E->value.frames.remove_at(p_idx); emit_changed(); } void SpriteFrames::clear(const StringName &p_anim) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().frames.clear(); + E->value.frames.clear(); emit_changed(); } @@ -124,37 +124,37 @@ Vector<String> SpriteFrames::get_animation_names() const { void SpriteFrames::set_animation_speed(const StringName &p_anim, double p_fps) { ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ")."); - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().speed = p_fps; + E->value.speed = p_fps; } double SpriteFrames::get_animation_speed(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().speed; + return E->value.speed; } void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); - E->get().loop = p_loop; + E->value.loop = p_loop; } bool SpriteFrames::get_animation_loop(const StringName &p_anim) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist."); - return E->get().loop; + return E->value.loop; } void SpriteFrames::_set_frames(const Array &p_frames) { clear_all(); - Map<StringName, Anim>::Element *E = animations.find(SceneStringNames::get_singleton()->_default); + HashMap<StringName, Anim>::Iterator E = animations.find(SceneStringNames::get_singleton()->_default); ERR_FAIL_COND(!E); - E->get().frames.resize(p_frames.size()); - for (int i = 0; i < E->get().frames.size(); i++) { - E->get().frames.write[i] = p_frames[i]; + E->value.frames.resize(p_frames.size()); + for (int i = 0; i < E->value.frames.size(); i++) { + E->value.frames.write[i] = p_frames[i]; } } diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index 12b69afde1..e32ccc1336 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -42,7 +42,7 @@ class SpriteFrames : public Resource { Vector<Ref<Texture2D>> frames; }; - Map<StringName, Anim> animations; + HashMap<StringName, Anim> animations; Array _get_frames() const; void _set_frames(const Array &p_frames); @@ -73,24 +73,24 @@ public: void add_frame(const StringName &p_anim, const Ref<Texture2D> &p_frame, int p_at_pos = -1); int get_frame_count(const StringName &p_anim) const; _FORCE_INLINE_ Ref<Texture2D> get_frame(const StringName &p_anim, int p_idx) const { - const Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::ConstIterator E = animations.find(p_anim); ERR_FAIL_COND_V_MSG(!E, Ref<Texture2D>(), "Animation '" + String(p_anim) + "' doesn't exist."); ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>()); - if (p_idx >= E->get().frames.size()) { + if (p_idx >= E->value.frames.size()) { return Ref<Texture2D>(); } - return E->get().frames[p_idx]; + return E->value.frames[p_idx]; } void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture2D> &p_frame) { - Map<StringName, Anim>::Element *E = animations.find(p_anim); + HashMap<StringName, Anim>::Iterator E = animations.find(p_anim); ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist."); ERR_FAIL_COND(p_idx < 0); - if (p_idx >= E->get().frames.size()) { + if (p_idx >= E->value.frames.size()) { return; } - E->get().frames.write[p_idx] = p_frame; + E->value.frames.write[p_idx] = p_frame; } void remove_frame(const StringName &p_anim, int p_idx); void clear(const StringName &p_anim); diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index 143f1679c6..1243a9dbf7 100644 --- a/scene/resources/syntax_highlighter.h +++ b/scene/resources/syntax_highlighter.h @@ -41,7 +41,7 @@ class SyntaxHighlighter : public Resource { GDCLASS(SyntaxHighlighter, Resource) private: - Map<int, Dictionary> highlighting_cache; + RBMap<int, Dictionary> highlighting_cache; void _lines_edited_from(int p_from_line, int p_to_line); protected: @@ -83,7 +83,7 @@ private: bool line_only = false; }; Vector<ColorRegion> color_regions; - Map<int, int> color_region_cache; + HashMap<int, int> color_region_cache; Dictionary keywords; Dictionary member_keywords; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index 337776fd47..f9390ca528 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -36,7 +36,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction); ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction); - ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Left-to-right,Right-to-left"), "set_direction", "get_direction"); ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextLine::set_orientation); ClassDB::bind_method(D_METHOD("get_orientation"), &TextLine::get_orientation); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 61adaf43dd..477b41efaa 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -139,7 +139,7 @@ void TextParagraph::_bind_methods() { void TextParagraph::_shape_lines() { if (lines_dirty) { - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { TS->free_rid(lines_rid[i]); } lines_rid.clear(); @@ -218,14 +218,14 @@ void TextParagraph::_shape_lines() { // Fill after min_size calculation. if (autowrap_enabled) { - int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); - bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); + int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); + bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size(); if (lines_hidden) { overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS; } if (alignment == HORIZONTAL_ALIGNMENT_FILL) { - for (int i = 0; i < lines_rid.size(); i++) { - if (i < visible_lines - 1 || lines_rid.size() == 1) { + for (int i = 0; i < (int)lines_rid.size(); i++) { + if (i < visible_lines - 1 || (int)lines_rid.size() == 1) { TS->shaped_text_fit_to_width(lines_rid[i], width, flags); } else if (i == (visible_lines - 1)) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); @@ -238,7 +238,7 @@ void TextParagraph::_shape_lines() { } else { // Autowrap disabled. - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { if (alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(lines_rid[i], width, flags); overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; @@ -258,8 +258,10 @@ RID TextParagraph::get_rid() const { } RID TextParagraph::get_line_rid(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), RID()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), RID()); return lines_rid[p_line]; } @@ -268,9 +270,11 @@ RID TextParagraph::get_dropcap_rid() const { } void TextParagraph::clear() { + _THREAD_SAFE_METHOD_ + spacing_top = 0; spacing_bottom = 0; - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { TS->free_rid(lines_rid[i]); } lines_rid.clear(); @@ -279,57 +283,79 @@ void TextParagraph::clear() { } void TextParagraph::set_preserve_invalid(bool p_enabled) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_preserve_invalid(rid, p_enabled); TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled); lines_dirty = true; } bool TextParagraph::get_preserve_invalid() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_preserve_invalid(rid); } void TextParagraph::set_preserve_control(bool p_enabled) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_preserve_control(rid, p_enabled); TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled); lines_dirty = true; } bool TextParagraph::get_preserve_control() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_preserve_control(rid); } void TextParagraph::set_direction(TextServer::Direction p_direction) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_direction(rid, p_direction); TS->shaped_text_set_direction(dropcap_rid, p_direction); lines_dirty = true; } TextServer::Direction TextParagraph::get_direction() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); return TS->shaped_text_get_direction(rid); } void TextParagraph::set_custom_punctuation(const String &p_punct) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_custom_punctuation(rid, p_punct); lines_dirty = true; } String TextParagraph::get_custom_punctuation() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_custom_punctuation(rid); } void TextParagraph::set_orientation(TextServer::Orientation p_orientation) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_orientation(rid, p_orientation); TS->shaped_text_set_orientation(dropcap_rid, p_orientation); lines_dirty = true; } TextServer::Orientation TextParagraph::get_orientation() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); return TS->shaped_text_get_orientation(rid); } bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(p_fonts.is_null(), false); TS->shaped_text_clear(dropcap_rid); dropcap_margins = p_dropcap_margins; @@ -339,12 +365,16 @@ bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, } void TextParagraph::clear_dropcap() { + _THREAD_SAFE_METHOD_ + dropcap_margins = Rect2(); TS->shaped_text_clear(dropcap_rid); lines_dirty = true; } bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V(p_fonts.is_null(), false); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); @@ -362,23 +392,31 @@ int TextParagraph::get_spacing_bottom() const { } void TextParagraph::set_bidi_override(const Array &p_override) { + _THREAD_SAFE_METHOD_ + TS->shaped_text_set_bidi_override(rid, p_override); lines_dirty = true; } bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { + _THREAD_SAFE_METHOD_ + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); lines_dirty = true; return res; } bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { + _THREAD_SAFE_METHOD_ + bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); lines_dirty = true; return res; } void TextParagraph::set_alignment(HorizontalAlignment p_alignment) { + _THREAD_SAFE_METHOD_ + if (alignment != p_alignment) { if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { alignment = p_alignment; @@ -394,11 +432,15 @@ HorizontalAlignment TextParagraph::get_alignment() const { } void TextParagraph::tab_align(const Vector<float> &p_tab_stops) { + _THREAD_SAFE_METHOD_ + tab_stops = p_tab_stops; lines_dirty = true; } void TextParagraph::set_flags(uint16_t p_flags) { + _THREAD_SAFE_METHOD_ + if (flags != p_flags) { flags = p_flags; lines_dirty = true; @@ -410,6 +452,8 @@ uint16_t TextParagraph::get_flags() const { } void TextParagraph::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) { + _THREAD_SAFE_METHOD_ + if (overrun_behavior != p_behavior) { overrun_behavior = p_behavior; lines_dirty = true; @@ -421,6 +465,8 @@ TextParagraph::OverrunBehavior TextParagraph::get_text_overrun_behavior() const } void TextParagraph::set_width(float p_width) { + _THREAD_SAFE_METHOD_ + if (width != p_width) { width = p_width; lines_dirty = true; @@ -432,6 +478,8 @@ float TextParagraph::get_width() const { } Size2 TextParagraph::get_non_wrapped_size() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); @@ -441,9 +489,11 @@ Size2 TextParagraph::get_non_wrapped_size() const { } Size2 TextParagraph::get_size() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Size2 size; - int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); + int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); for (int i = 0; i < visible_lines; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { @@ -458,11 +508,15 @@ Size2 TextParagraph::get_size() const { } int TextParagraph::get_line_count() const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - return lines_rid.size(); + return (int)lines_rid.size(); } void TextParagraph::set_max_lines_visible(int p_lines) { + _THREAD_SAFE_METHOD_ + if (p_lines != max_lines_visible) { max_lines_visible = p_lines; lines_dirty = true; @@ -474,14 +528,18 @@ int TextParagraph::get_max_lines_visible() const { } Array TextParagraph::get_line_objects(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Array()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Array()); return TS->shaped_text_get_objects(lines_rid[p_line]); } Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Rect2()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2()); Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key); for (int i = 0; i < p_line; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); @@ -495,8 +553,10 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { } Size2 TextParagraph::get_line_size(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Size2()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2()); if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom); } else { @@ -505,42 +565,56 @@ Size2 TextParagraph::get_line_size(int p_line) const { } Vector2i TextParagraph::get_line_range(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), Vector2i()); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Vector2i()); return TS->shaped_text_get_range(lines_rid[p_line]); } float TextParagraph::get_line_ascent(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; } float TextParagraph::get_line_descent(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom; } float TextParagraph::get_line_width(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_width(lines_rid[p_line]); } float TextParagraph::get_line_underline_position(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_underline_position(lines_rid[p_line]); } float TextParagraph::get_line_underline_thickness(int p_line) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND_V(p_line < 0 || p_line >= lines_rid.size(), 0.f); + ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); return TS->shaped_text_get_underline_thickness(lines_rid[p_line]); } Size2 TextParagraph::get_dropcap_size() const { + _THREAD_SAFE_METHOD_ + return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position; } @@ -549,6 +623,8 @@ int TextParagraph::get_dropcap_lines() const { } void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs = p_pos; float h_offset = 0.f; @@ -571,7 +647,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color); } - int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size(); + int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size(); for (int i = 0; i < lines_visible; i++) { float l_width = width; @@ -650,6 +726,8 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo } void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs = p_pos; @@ -673,7 +751,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color); } - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { float l_width = width; if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; @@ -750,6 +828,8 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli } int TextParagraph::hit_test(const Point2 &p_coords) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); Vector2 ofs; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -761,7 +841,7 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { return 0; } } - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) { return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x); @@ -778,6 +858,8 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { } void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + Vector2 ofs = p_pos; float h_offset = 0.f; if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -800,6 +882,8 @@ void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color } void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + Vector2 ofs = p_pos; float h_offset = 0.f; if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) { @@ -822,8 +906,10 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int } void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size()); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); Vector2 ofs = p_pos; @@ -836,8 +922,10 @@ void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, co } void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color) const { + _THREAD_SAFE_METHOD_ + const_cast<TextParagraph *>(this)->_shape_lines(); - ERR_FAIL_COND(p_line < 0 || p_line >= lines_rid.size()); + ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size()); Vector2 ofs = p_pos; if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { @@ -862,7 +950,7 @@ TextParagraph::TextParagraph() { } TextParagraph::~TextParagraph() { - for (int i = 0; i < lines_rid.size(); i++) { + for (int i = 0; i < (int)lines_rid.size(); i++) { TS->free_rid(lines_rid[i]); } lines_rid.clear(); diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 8a8a53943b..4f1aad16b5 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -31,6 +31,7 @@ #ifndef TEXT_PARAGRAPH_H #define TEXT_PARAGRAPH_H +#include "core/templates/local_vector.h" #include "scene/resources/font.h" #include "servers/text_server.h" @@ -38,6 +39,7 @@ class TextParagraph : public RefCounted { GDCLASS(TextParagraph, RefCounted); + _THREAD_SAFE_CLASS_ public: enum OverrunBehavior { @@ -54,7 +56,7 @@ private: Rect2 dropcap_margins; RID rid; - Vector<RID> lines_rid; + LocalVector<RID> lines_rid; int spacing_top = 0; int spacing_bottom = 0; @@ -156,6 +158,8 @@ public: int hit_test(const Point2 &p_coords) const; + Mutex &get_mutex() const { return _thread_safe_; }; + TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); ~TextParagraph(); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 5850d253e3..9442a58ac1 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -175,8 +175,8 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { } void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::OBJECT, PNAME("image"), PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("size"), PROPERTY_HINT_NONE, "")); } void ImageTexture::create_from_image(const Ref<Image> &p_image) { diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 10806aef45..2981f38766 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -129,42 +129,42 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } // Icons. - for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) { + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/icons/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } // Styles. - for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) { + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/styles/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } // Fonts. - for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) { + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { for (const KeyValue<StringName, Ref<Font>> &F : E.value) { list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/fonts/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); } } // Font sizes. - for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) { + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { for (const KeyValue<StringName, int> &F : E.value) { list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/font_sizes/" + F.key, PROPERTY_HINT_RANGE, "0,256,1,or_greater")); } } // Colors. - for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) { + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { for (const KeyValue<StringName, Color> &F : E.value) { - list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/colors/" + F.key)); + list.push_back(PropertyInfo(Variant::COLOR, String() + E.key + "/colors/" + F.key)); } } // Constants. - for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) { + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { for (const KeyValue<StringName, int> &F : E.value) { list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key)); } @@ -172,7 +172,15 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { // Sort and store properties. list.sort(); + String prev_type; for (const PropertyInfo &E : list) { + // Add groups for types so that their names are left unchanged in the inspector. + String current_type = E.name.get_slice("/", 0); + if (prev_type != current_type) { + p_list->push_back(PropertyInfo(Variant::NIL, current_type, PROPERTY_HINT_NONE, current_type + "/", PROPERTY_USAGE_GROUP)); + prev_type = current_type; + } + p_list->push_back(E); } } @@ -399,7 +407,7 @@ void Theme::add_icon_type(const StringName &p_theme_type) { if (icon_map.has(p_theme_type)) { return; } - icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>(); + icon_map[p_theme_type] = ThemeIconMap(); } void Theme::remove_icon_type(const StringName &p_theme_type) { @@ -424,7 +432,7 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { void Theme::get_icon_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) { + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { p_list->push_back(E.key); } } @@ -509,7 +517,7 @@ void Theme::add_stylebox_type(const StringName &p_theme_type) { if (style_map.has(p_theme_type)) { return; } - style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>(); + style_map[p_theme_type] = ThemeStyleMap(); } void Theme::remove_stylebox_type(const StringName &p_theme_type) { @@ -534,7 +542,7 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { void Theme::get_stylebox_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) { + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { p_list->push_back(E.key); } } @@ -621,7 +629,7 @@ void Theme::add_font_type(const StringName &p_theme_type) { if (font_map.has(p_theme_type)) { return; } - font_map[p_theme_type] = HashMap<StringName, Ref<Font>>(); + font_map[p_theme_type] = ThemeFontMap(); } void Theme::remove_font_type(const StringName &p_theme_type) { @@ -646,7 +654,7 @@ void Theme::remove_font_type(const StringName &p_theme_type) { void Theme::get_font_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) { + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { p_list->push_back(E.key); } } @@ -720,7 +728,7 @@ void Theme::add_font_size_type(const StringName &p_theme_type) { if (font_size_map.has(p_theme_type)) { return; } - font_size_map[p_theme_type] = HashMap<StringName, int>(); + font_size_map[p_theme_type] = ThemeFontSizeMap(); } void Theme::remove_font_size_type(const StringName &p_theme_type) { @@ -734,7 +742,7 @@ void Theme::remove_font_size_type(const StringName &p_theme_type) { void Theme::get_font_size_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) { + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { p_list->push_back(E.key); } } @@ -806,7 +814,7 @@ void Theme::add_color_type(const StringName &p_theme_type) { if (color_map.has(p_theme_type)) { return; } - color_map[p_theme_type] = HashMap<StringName, Color>(); + color_map[p_theme_type] = ThemeColorMap(); } void Theme::remove_color_type(const StringName &p_theme_type) { @@ -820,7 +828,7 @@ void Theme::remove_color_type(const StringName &p_theme_type) { void Theme::get_color_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) { + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { p_list->push_back(E.key); } } @@ -892,7 +900,7 @@ void Theme::add_constant_type(const StringName &p_theme_type) { if (constant_map.has(p_theme_type)) { return; } - constant_map[p_theme_type] = HashMap<StringName, int>(); + constant_map[p_theme_type] = ThemeConstantMap(); } void Theme::remove_constant_type(const StringName &p_theme_type) { @@ -906,7 +914,7 @@ void Theme::remove_constant_type(const StringName &p_theme_type) { void Theme::get_constant_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) { + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { p_list->push_back(E.key); } } @@ -1264,12 +1272,44 @@ void Theme::remove_type(const StringName &p_theme_type) { void Theme::get_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - get_icon_type_list(p_list); - get_stylebox_type_list(p_list); - get_font_type_list(p_list); - get_font_size_type_list(p_list); - get_color_type_list(p_list); - get_constant_type_list(p_list); + // This Set guarantees uniqueness. + // Because each map can have the same type defined, but for this method + // we only want one occurrence of each type. + RBSet<StringName> types; + + // Icons. + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { + types.insert(E.key); + } + + // Styles. + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { + types.insert(E.key); + } + + // Fonts. + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { + types.insert(E.key); + } + + // Font sizes. + for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) { + types.insert(E.key); + } + + // Colors. + for (const KeyValue<StringName, ThemeColorMap> &E : color_map) { + types.insert(E.key); + } + + // Constants. + for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) { + types.insert(E.key); + } + + for (const StringName &E : types) { + p_list->push_back(E); + } } void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { @@ -1580,7 +1620,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Colors. { - for (const KeyValue<StringName, HashMap<StringName, Color>> &E : p_other->color_map) { + for (const KeyValue<StringName, ThemeColorMap> &E : p_other->color_map) { for (const KeyValue<StringName, Color> &F : E.value) { set_color(F.key, E.key, F.value); } @@ -1589,7 +1629,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Constants. { - for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->constant_map) { + for (const KeyValue<StringName, ThemeConstantMap> &E : p_other->constant_map) { for (const KeyValue<StringName, int> &F : E.value) { set_constant(F.key, E.key, F.value); } @@ -1598,7 +1638,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Fonts. { - for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : p_other->font_map) { + for (const KeyValue<StringName, ThemeFontMap> &E : p_other->font_map) { for (const KeyValue<StringName, Ref<Font>> &F : E.value) { set_font(F.key, E.key, F.value); } @@ -1607,7 +1647,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Font sizes. { - for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->font_size_map) { + for (const KeyValue<StringName, ThemeFontSizeMap> &E : p_other->font_size_map) { for (const KeyValue<StringName, int> &F : E.value) { set_font_size(F.key, E.key, F.value); } @@ -1616,7 +1656,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Icons. { - for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : p_other->icon_map) { + for (const KeyValue<StringName, ThemeIconMap> &E : p_other->icon_map) { for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { set_icon(F.key, E.key, F.value); } @@ -1625,7 +1665,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Styleboxes. { - for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : p_other->style_map) { + for (const KeyValue<StringName, ThemeStyleMap> &E : p_other->style_map) { for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { set_stylebox(F.key, E.key, F.value); } @@ -1645,7 +1685,7 @@ void Theme::merge_with(const Ref<Theme> &p_other) { void Theme::clear() { // These items need disconnecting. { - for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) { + for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) { for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { if (F.value.is_valid()) { Ref<Texture2D> icon = F.value; @@ -1656,7 +1696,7 @@ void Theme::clear() { } { - for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) { + for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) { for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { if (F.value.is_valid()) { Ref<StyleBox> style = F.value; @@ -1667,7 +1707,7 @@ void Theme::clear() { } { - for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) { + for (const KeyValue<StringName, ThemeFontMap> &E : font_map) { for (const KeyValue<StringName, Ref<Font>> &F : E.value) { if (F.value.is_valid()) { Ref<Font> font = F.value; diff --git a/scene/resources/theme.h b/scene/resources/theme.h index f8f1e95634..87d7d2fdea 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -47,6 +47,13 @@ class Theme : public Resource { #endif public: + using ThemeIconMap = HashMap<StringName, Ref<Texture2D>>; + using ThemeStyleMap = HashMap<StringName, Ref<StyleBox>>; + using ThemeFontMap = HashMap<StringName, Ref<Font>>; + using ThemeFontSizeMap = HashMap<StringName, int>; + using ThemeColorMap = HashMap<StringName, Color>; + using ThemeConstantMap = HashMap<StringName, int>; + enum DataType { DATA_TYPE_COLOR, DATA_TYPE_CONSTANT, @@ -62,12 +69,12 @@ private: void _emit_theme_changed(bool p_notify_list_changed = false); - HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map; - HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map; - HashMap<StringName, HashMap<StringName, Ref<Font>>> font_map; - HashMap<StringName, HashMap<StringName, int>> font_size_map; - HashMap<StringName, HashMap<StringName, Color>> color_map; - HashMap<StringName, HashMap<StringName, int>> constant_map; + HashMap<StringName, ThemeIconMap> icon_map; + HashMap<StringName, ThemeStyleMap> style_map; + HashMap<StringName, ThemeFontMap> font_map; + HashMap<StringName, ThemeFontSizeMap> font_size_map; + HashMap<StringName, ThemeColorMap> color_map; + HashMap<StringName, ThemeConstantMap> constant_map; HashMap<StringName, StringName> variation_map; HashMap<StringName, List<StringName>> variation_base_map; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 67b82af617..e22b8efaae 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1345,19 +1345,19 @@ int TileSet::get_patterns_count() { return patterns.size(); } -Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>()); +RBSet<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileSet::TerrainsPattern>()); _update_terrains_cache(); - Set<TileSet::TerrainsPattern> output; - for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) { + RBSet<TileSet::TerrainsPattern> output; + for (KeyValue<TileSet::TerrainsPattern, RBSet<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) { output.insert(kv.key); } return output; } -Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>()); +RBSet<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet<TileMapCell>()); _update_terrains_cache(); return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; } @@ -1368,13 +1368,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti // Count the sum of probabilities. double sum = 0.0; - Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; - for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { - if (E->get().source_id >= 0) { - Ref<TileSetSource> source = sources[E->get().source_id]; + RBSet<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; + for (const TileMapCell &E : set) { + if (E.source_id >= 0) { + Ref<TileSetSource> source = sources[E.source_id]; Ref<TileSetAtlasSource> atlas_source = source; if (atlas_source.is_valid()) { - TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile); + TileData *tile_data = atlas_source->get_tile_data(E.get_atlas_coords(), E.alternative_tile); sum += tile_data->get_probability(); } else { sum += 1.0; @@ -1389,13 +1389,13 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti double picked = Math::random(0.0, sum); // Pick the tile. - for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) { - if (E->get().source_id >= 0) { - Ref<TileSetSource> source = sources[E->get().source_id]; + for (const TileMapCell &E : set) { + if (E.source_id >= 0) { + Ref<TileSetSource> source = sources[E.source_id]; Ref<TileSetAtlasSource> atlas_source = source; if (atlas_source.is_valid()) { - TileData *tile_data = atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile); + TileData *tile_data = atlas_source->get_tile_data(E.get_atlas_coords(), E.alternative_tile); count += tile_data->get_probability(); } else { count += 1.0; @@ -1405,7 +1405,7 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti } if (count >= picked) { - return E->get(); + return E; } } @@ -2391,7 +2391,7 @@ void TileSet::_compatibility_conversion() { value_array.push_back(alternative_tile); if (!compatibility_tilemap_mapping.has(E.key)) { - compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); + compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>(); } compatibility_tilemap_mapping[E.key][key_array] = value_array; compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; @@ -2483,7 +2483,7 @@ void TileSet::_compatibility_conversion() { value_array.push_back(alternative_tile); if (!compatibility_tilemap_mapping.has(E.key)) { - compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); + compatibility_tilemap_mapping[E.key] = RBMap<Array, Array>(); } compatibility_tilemap_mapping[E.key][key_array] = value_array; compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; @@ -2571,7 +2571,7 @@ void TileSet::_compatibility_conversion() { for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { memdelete(E.value); } - compatibility_data = Map<int, CompatibilityTileData *>(); + compatibility_data = HashMap<int, CompatibilityTileData *>(); } Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose) { @@ -2622,12 +2622,12 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { // Get or create the compatibility object CompatibilityTileData *ctd; - Map<int, CompatibilityTileData *>::Element *E = compatibility_data.find(id); + HashMap<int, CompatibilityTileData *>::Iterator E = compatibility_data.find(id); if (!E) { ctd = memnew(CompatibilityTileData); compatibility_data.insert(id, ctd); } else { - ctd = E->get(); + ctd = E->value; } if (components.size() < 2) { @@ -3099,7 +3099,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Terrains. p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match Corners and Sides,Match Corners,Match Sides")); p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index))); for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 95de46c9ab..633e1f4bed 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -69,6 +69,11 @@ union TileMapCell { }; uint64_t _u64t; + + static uint32_t hash(const TileMapCell &p_hash) { + return hash_one_uint64(p_hash._u64t); + } + TileMapCell(int p_source_id = -1, Vector2i p_atlas_coords = Vector2i(-1, -1), int p_alternative_tile = -1) { // default are INVALID_SOURCE, INVALID_ATLAS_COORDS, INVALID_TILE_ALTERNATIVE source_id = p_source_id; set_atlas_coords(p_atlas_coords); @@ -103,13 +108,16 @@ union TileMapCell { bool operator!=(const TileMapCell &p_other) const { return !(source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile); } + bool operator==(const TileMapCell &p_other) const { + return source_id == p_other.source_id && coord_x == p_other.coord_x && coord_y == p_other.coord_y && alternative_tile == p_other.alternative_tile; + } }; class TileMapPattern : public Resource { GDCLASS(TileMapPattern, Resource); Vector2i size; - Map<Vector2i, TileMapCell> pattern; + HashMap<Vector2i, TileMapCell> pattern; void _set_tile_data(const Vector<int> &p_data); Vector<int> _get_tile_data() const; @@ -166,11 +174,11 @@ private: Size2i autotile_tile_size = Size2i(16, 16); int autotile_spacing = 0; - Map<Vector2i, int> autotile_bitmask_flags; - Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; - Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; - Map<Vector2i, int> autotile_priority_map; - Map<Vector2i, int> autotile_z_index_map; + HashMap<Vector2i, int> autotile_bitmask_flags; + HashMap<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; + HashMap<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; + HashMap<Vector2i, int> autotile_priority_map; + HashMap<Vector2i, int> autotile_z_index_map; Vector<CompatibilityShapeData> shapes; Ref<OccluderPolygon2D> occluder; @@ -186,9 +194,9 @@ private: COMPATIBILITY_TILE_MODE_ATLAS_TILE, }; - Map<int, CompatibilityTileData *> compatibility_data; - Map<int, int> compatibility_tilemap_mapping_tile_modes; - Map<int, Map<Array, Array>> compatibility_tilemap_mapping; + HashMap<int, CompatibilityTileData *> compatibility_data; + HashMap<int, int> compatibility_tilemap_mapping_tile_modes; + HashMap<int, RBMap<Array, Array>> compatibility_tilemap_mapping; void _compatibility_conversion(); @@ -324,10 +332,10 @@ private: }; Vector<TerrainSet> terrain_sets; - Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; + HashMap<TerrainMode, HashMap<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes; bool terrain_bits_meshes_dirty = true; - LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data. + LocalVector<RBMap<TileSet::TerrainsPattern, RBSet<TileMapCell>>> per_terrain_pattern_tiles; // Cached data. bool terrains_cache_dirty = true; void _update_terrains_cache(); @@ -343,10 +351,10 @@ private: Variant::Type type = Variant::NIL; }; Vector<CustomDataLayer> custom_data_layers; - Map<String, int> custom_data_layers_by_name; + HashMap<String, int> custom_data_layers_by_name; // Per Atlas source data. - Map<int, Ref<TileSetSource>> sources; + HashMap<int, Ref<TileSetSource>> sources; Vector<int> source_ids; int next_source_id = 0; // --------------------- @@ -357,9 +365,9 @@ private: void _source_changed(); // Tile proxies - Map<int, int> source_level_proxies; - Map<Array, Array> coords_level_proxies; - Map<Array, Array> alternative_level_proxies; + RBMap<int, int> source_level_proxies; + RBMap<Array, Array> coords_level_proxies; + RBMap<Array, Array> alternative_level_proxies; // Helpers Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); @@ -499,8 +507,8 @@ public: int get_patterns_count(); // Terrains. - Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set); - Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); + RBSet<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set); + RBSet<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); TileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); // Helpers @@ -579,7 +587,7 @@ private: LocalVector<real_t> animation_frames_durations; // Alternatives - Map<int, TileData *> alternatives; + HashMap<int, TileData *> alternatives; Vector<int> alternatives_ids; int next_alternative_id = 1; }; @@ -589,9 +597,9 @@ private: Vector2i separation; Size2i texture_region_size = Size2i(16, 16); - Map<Vector2i, TileAlternativesData> tiles; + HashMap<Vector2i, TileAlternativesData> tiles; Vector<Vector2i> tiles_ids; - Map<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile + HashMap<Vector2i, Vector2i> _coords_mapping_cache; // Maps any coordinate to the including tile TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile); const TileData *_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const; @@ -716,7 +724,7 @@ private: bool display_placeholder = false; }; Vector<int> scenes_ids; - Map<int, SceneData> scenes; + HashMap<int, SceneData> scenes; int next_scene_id = 1; void _compute_next_alternative_id(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 6848011228..d361aa876b 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -345,8 +345,8 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const { return Vector<StringName>(); } -Map<StringName, String> VisualShaderNode::get_editable_properties_names() const { - return Map<StringName, String>(); +HashMap<StringName, String> VisualShaderNode::get_editable_properties_names() const { + return HashMap<StringName, String>(); } Array VisualShaderNode::get_default_input_values() const { @@ -1178,16 +1178,16 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port StringBuilder global_code; StringBuilder global_code_per_node; - Map<Type, StringBuilder> global_code_per_func; + HashMap<Type, StringBuilder> global_code_per_func; StringBuilder code; - Set<StringName> classes; + RBSet<StringName> classes; global_code += String() + "shader_type canvas_item;\n"; String global_expressions; for (int i = 0, index = 0; i < TYPE_MAX; i++) { - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr()); + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node; if (global_expression.is_valid()) { String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; @@ -1222,7 +1222,7 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port code += "\nvoid fragment() {\n"; - Set<int> processed; + RBSet<int> processed; Error err = _write_node(p_type, &global_code, &global_code_per_node, &global_code_per_func, code, default_tex_params, input_connections, output_connections, p_node, processed, true, classes); ERR_FAIL_COND_V(err != OK, String()); @@ -1547,11 +1547,11 @@ void VisualShader::reset_state() { } void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { //mode - p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); + p_list->push_back(PropertyInfo(Variant::INT, PNAME("mode"), PROPERTY_HINT_ENUM, "Node3D,CanvasItem,Particles,Sky,Fog")); //render modes - Map<String, String> blend_mode_enums; - Set<String> toggles; + HashMap<String, String> blend_mode_enums; + RBSet<String> toggles; const Vector<ShaderLanguage::ModeInfo> &rmodes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader_mode)); @@ -1562,7 +1562,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { const String begin = String(info.name); for (int j = 0; j < info.options.size(); j++) { - const String option = String(info.options[j]); + const String option = String(info.options[j]).capitalize(); if (!blend_mode_enums.has(begin)) { blend_mode_enums[begin] = option; @@ -1576,15 +1576,15 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } for (const KeyValue<String, String> &E : blend_mode_enums) { - p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value)); + p_list->push_back(PropertyInfo(Variant::INT, vformat("%s/%s", PNAME("modes"), E.key), PROPERTY_HINT_ENUM, E.value)); } - for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get())); + for (const String &E : toggles) { + p_list->push_back(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("flags"), E))); } for (const KeyValue<String, Varying> &E : varyings) { - p_list->push_back(PropertyInfo(Variant::STRING, "varyings/" + E.key)); + p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key))); } for (int i = 0; i < TYPE_MAX; i++) { @@ -1611,7 +1611,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { } } -Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBuilder *global_code_per_node, Map<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const { +Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, RBSet<int> &processed, bool for_preview, RBSet<StringName> &r_classes) const { const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node; if (vsnode->is_disabled()) { @@ -1903,7 +1903,7 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui int output_count = vsnode->get_output_port_count(); int initial_output_count = output_count; - Map<int, bool> expanded_output_ports; + HashMap<int, bool> expanded_output_ports; for (int i = 0; i < initial_output_count; i++) { bool expanded = false; @@ -2133,11 +2133,11 @@ void VisualShader::_update_shader() const { StringBuilder global_code; StringBuilder global_code_per_node; - Map<Type, StringBuilder> global_code_per_func; + HashMap<Type, StringBuilder> global_code_per_func; StringBuilder code; Vector<VisualShader::DefaultTextureParam> default_tex_params; - Set<StringName> classes; - Map<int, int> insertion_pos; + RBSet<StringName> classes; + HashMap<int, int> insertion_pos; static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles", "sky", "fog" }; global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n"; @@ -2181,18 +2181,18 @@ void VisualShader::_update_shader() const { static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light", "start", "process", "collide", "start_custom", "process_custom", "sky", "fog" }; String global_expressions; - Set<String> used_uniform_names; + RBSet<String> used_uniform_names; List<VisualShaderNodeUniform *> uniforms; - Map<int, List<int>> emitters; - Map<int, List<int>> varying_setters; + HashMap<int, List<int>> emitters; + HashMap<int, List<int>> varying_setters; for (int i = 0, index = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { continue; } - for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { - Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr()); + for (const KeyValue<int, Node> &E : graph[i].nodes) { + Ref<VisualShaderNodeGlobalExpression> global_expression = E.value.node; if (global_expression.is_valid()) { String expr = ""; expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n"; @@ -2201,27 +2201,27 @@ void VisualShader::_update_shader() const { expr += "\n"; global_expressions += expr; } - Ref<VisualShaderNodeUniformRef> uniform_ref = Object::cast_to<VisualShaderNodeUniformRef>(E->get().node.ptr()); + Ref<VisualShaderNodeUniformRef> uniform_ref = E.value.node; if (uniform_ref.is_valid()) { used_uniform_names.insert(uniform_ref->get_uniform_name()); } - Ref<VisualShaderNodeUniform> uniform = Object::cast_to<VisualShaderNodeUniform>(E->get().node.ptr()); + Ref<VisualShaderNodeUniform> uniform = E.value.node; if (uniform.is_valid()) { uniforms.push_back(uniform.ptr()); } - Ref<VisualShaderNodeVaryingSetter> varying_setter = Object::cast_to<VisualShaderNodeVaryingSetter>(E->get().node.ptr()); + Ref<VisualShaderNodeVaryingSetter> varying_setter = E.value.node; if (varying_setter.is_valid() && varying_setter->is_input_port_connected(0)) { if (!varying_setters.has(i)) { varying_setters.insert(i, List<int>()); } - varying_setters[i].push_back(E->key()); + varying_setters[i].push_back(E.key); } - Ref<VisualShaderNodeParticleEmit> emit_particle = Object::cast_to<VisualShaderNodeParticleEmit>(E->get().node.ptr()); + Ref<VisualShaderNodeParticleEmit> emit_particle = E.value.node; if (emit_particle.is_valid()) { if (!emitters.has(i)) { emitters.insert(i, List<int>()); } - emitters[i].push_back(E->key()); + emitters[i].push_back(E.key); } } } @@ -2269,8 +2269,8 @@ void VisualShader::_update_shader() const { global_code += "\n"; } - Map<int, String> code_map; - Set<int> empty_funcs; + HashMap<int, String> code_map; + RBSet<int> empty_funcs; for (int i = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { @@ -2282,7 +2282,7 @@ void VisualShader::_update_shader() const { VMap<ConnectionKey, const List<Connection>::Element *> output_connections; StringBuilder func_code; - Set<int> processed; + RBSet<int> processed; bool is_empty_func = false; if (shader_mode != Shader::MODE_PARTICLES && shader_mode != Shader::MODE_SKY && shader_mode != Shader::MODE_FOG) { diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index aaf570d98c..aa5263943b 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -127,7 +127,7 @@ private: }; struct Graph { - Map<int, Node> nodes; + RBMap<int, Node> nodes; List<Connection> connections; } graph[TYPE_MAX]; @@ -139,9 +139,9 @@ private: Vector2 graph_offset; HashMap<String, int> modes; - Set<StringName> flags; + RBSet<StringName> flags; - Map<String, Varying> varyings; + HashMap<String, Varying> varyings; List<Varying> varyings_list; mutable SafeFlag dirty; @@ -158,7 +158,7 @@ private: } }; - Error _write_node(Type p_type, StringBuilder *global_code, StringBuilder *global_code_per_node, Map<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const; + Error _write_node(Type p_type, StringBuilder *global_code, StringBuilder *global_code_per_node, HashMap<Type, StringBuilder> *global_code_per_func, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, RBSet<int> &processed, bool for_preview, RBSet<StringName> &r_classes) const; void _input_type_changed(Type p_type, int p_id); bool has_func_name(RenderingServer::ShaderMode p_mode, const String &p_func_name) const; @@ -255,10 +255,10 @@ class VisualShaderNode : public Resource { int port_preview = -1; - Map<int, Variant> default_input_values; - Map<int, bool> connected_input_ports; - Map<int, int> connected_output_ports; - Map<int, bool> expanded_output_ports; + HashMap<int, Variant> default_input_values; + HashMap<int, bool> connected_input_ports; + HashMap<int, int> connected_output_ports; + HashMap<int, bool> expanded_output_ports; protected: bool simple_decl = true; @@ -328,7 +328,7 @@ public: void set_disabled(bool p_disabled = true); virtual Vector<StringName> get_editable_properties() const; - virtual Map<StringName, String> get_editable_properties_names() const; + virtual HashMap<StringName, String> get_editable_properties_names() const; virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; @@ -680,9 +680,9 @@ protected: String name; }; - Map<int, Port> input_ports; - Map<int, Port> output_ports; - Map<int, Control *> controls; + HashMap<int, Port> input_ports; + HashMap<int, Port> output_ports; + HashMap<int, Control *> controls; protected: static void _bind_methods(); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 6af34a8a82..dbd45793f9 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -5782,8 +5782,8 @@ bool VisualShaderNodeTextureUniform::is_show_prop_names() const { return true; } -Map<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { - Map<StringName, String> names; +HashMap<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const { + HashMap<StringName, String> names; names.insert("texture_type", RTR("Type")); names.insert("color_default", RTR("Default Color")); names.insert("texture_filter", RTR("Filter")); diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 338f1157d3..b159d25eba 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -2175,7 +2175,7 @@ public: virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override; virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override; - virtual Map<StringName, String> get_editable_properties_names() const override; + virtual HashMap<StringName, String> get_editable_properties_names() const override; virtual bool is_show_prop_names() const override; Vector<StringName> get_editable_properties() const override; diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index 0879f2e735..54df935168 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -74,8 +74,8 @@ Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() co return props; } -Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const { - Map<StringName, String> names; +HashMap<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const { + HashMap<StringName, String> names; names.insert("mode_2d", RTR("2D Mode")); return names; } @@ -704,8 +704,8 @@ Vector<StringName> VisualShaderNodeParticleMeshEmitter::get_editable_properties( return props; } -Map<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const { - Map<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names(); +HashMap<StringName, String> VisualShaderNodeParticleMeshEmitter::get_editable_properties_names() const { + HashMap<StringName, String> names = VisualShaderNodeParticleEmitter::get_editable_properties_names(); names.insert("mesh", RTR("Mesh")); names.insert("use_all_surfaces", RTR("Use All Surfaces")); diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h index 0b91cba5e0..05a059373b 100644 --- a/scene/resources/visual_shader_particle_nodes.h +++ b/scene/resources/visual_shader_particle_nodes.h @@ -52,7 +52,7 @@ public: bool is_mode_2d() const; Vector<StringName> get_editable_properties() const override; - virtual Map<StringName, String> get_editable_properties_names() const override; + virtual HashMap<StringName, String> get_editable_properties_names() const override; bool is_show_prop_names() const override; VisualShaderNodeParticleEmitter(); @@ -153,7 +153,7 @@ public: int get_surface_index() const; Vector<StringName> get_editable_properties() const override; - Map<StringName, String> get_editable_properties_names() const override; + HashMap<StringName, String> get_editable_properties_names() const override; Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override; VisualShaderNodeParticleMeshEmitter(); diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index 4a277c3d84..71ae40ec82 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -46,7 +46,7 @@ class World2D : public Resource { RID space; RID navigation_map; - Set<Viewport *> viewports; + RBSet<Viewport *> viewports; protected: static void _bind_methods(); @@ -62,7 +62,7 @@ public: PhysicsDirectSpaceState2D *get_direct_space_state(); - _FORCE_INLINE_ const Set<Viewport *> &get_viewports() { return viewports; } + _FORCE_INLINE_ const RBSet<Viewport *> &get_viewports() { return viewports; } World2D(); ~World2D(); diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index b34b7a2bfb..18d28e812f 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -53,7 +53,7 @@ private: Ref<Environment> fallback_environment; Ref<CameraEffects> camera_effects; - Set<Camera3D *> cameras; + RBSet<Camera3D *> cameras; protected: static void _bind_methods(); @@ -77,7 +77,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - _FORCE_INLINE_ const Set<Camera3D *> &get_cameras() const { return cameras; } + _FORCE_INLINE_ const RBSet<Camera3D *> &get_cameras() const { return cameras; } PhysicsDirectSpaceState3D *get_direct_space_state(); |