diff options
Diffstat (limited to 'scene')
245 files changed, 8941 insertions, 3680 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index d3783aadd1..4734f97e23 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -443,6 +443,17 @@ TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const { return warnings; } +void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0 && p_function == "play" && frames.is_valid()) { + List<StringName> al; + frames->get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimatedSprite2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite2D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite2D::get_sprite_frames); diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index b3af931ea2..3a41f810dc 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -109,6 +109,8 @@ public: bool is_flipped_v() const; TypedArray<String> get_configuration_warnings() const override; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimatedSprite2D(); }; 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 68047ccebf..a584420ced 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -49,7 +49,7 @@ public: private: SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED; Vector2 gravity_vec; - real_t gravity; + real_t gravity = 0.0; bool gravity_is_point = false; real_t gravity_distance_scale = 0.0; @@ -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_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/camera_2d.cpp b/scene/2d/camera_2d.cpp index efde8d8a2b..f61dbc071d 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -57,7 +57,7 @@ void Camera2D::_update_scroll() { Size2 screen_size = _get_camera_screen_size(); Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset); + get_tree()->call_group(group_name, "_camera_moved", xform, screen_offset); }; } @@ -421,7 +421,7 @@ bool Camera2D::is_current() const { void Camera2D::make_current() { if (is_inside_tree()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", this); + get_tree()->call_group(group_name, "_make_current", this); } else { current = true; } @@ -430,7 +430,7 @@ void Camera2D::make_current() { void Camera2D::clear_current() { if (is_inside_tree()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_make_current", (Object *)nullptr); + get_tree()->call_group(group_name, "_make_current", (Object *)nullptr); } else { current = false; } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index c503de2d59..a8c12f4893 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -81,7 +81,7 @@ void CollisionObject2D::_notification(int p_what) { return; } - Transform2D global_transform = get_global_transform_with_canvas(); + Transform2D global_transform = get_global_transform(); if (area) { PhysicsServer2D::get_singleton()->area_set_transform(rid, global_transform); @@ -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/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index c8986e3c94..20840f5aea 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -32,6 +32,7 @@ #include "collision_object_2d.h" #include "core/math/geometry_2d.h" +#include "scene/2d/area_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -254,6 +255,9 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const { warnings.push_back(RTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode.")); } } + if (one_way_collision && Object::cast_to<Area2D>(get_parent())) { + warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D.")); + } return warnings; } @@ -276,6 +280,7 @@ void CollisionPolygon2D::set_one_way_collision(bool p_enable) { if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } + update_configuration_warnings(); } bool CollisionPolygon2D::is_one_way_collision_enabled() const { diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index dd47ae6cb5..5a25b1705a 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -31,6 +31,7 @@ #include "collision_shape_2d.h" #include "collision_object_2d.h" +#include "scene/2d/area_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -176,6 +177,9 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const { if (!shape.is_valid()) { warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!")); } + if (one_way_collision && Object::cast_to<Area2D>(get_parent())) { + warnings.push_back(RTR("The One Way Collision property will be ignored when the parent is an Area2D.")); + } Ref<ConvexPolygonShape2D> convex = shape; Ref<ConcavePolygonShape2D> concave = shape; @@ -204,6 +208,7 @@ void CollisionShape2D::set_one_way_collision(bool p_enable) { if (parent) { parent->shape_owner_set_one_way_collision(owner_id, p_enable); } + update_configuration_warnings(); } bool CollisionShape2D::is_one_way_collision_enabled() const { diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index bad1488d5a..f5c14b6845 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -743,7 +743,7 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); - p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf()); + p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf()); real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); p.rotation = Math::deg2rad(base_angle); @@ -788,8 +788,8 @@ void CPUParticles2D::_particles_process(double p_delta) { if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) { Vector2 normal = emission_normals.get(random_idx); Transform2D m2; - m2.set_axis(0, normal); - m2.set_axis(1, normal.orthogonal()); + m2.columns[0] = normal; + m2.columns[1] = normal.orthogonal(); p.velocity = m2.basis_xform(p.velocity); } @@ -969,13 +969,13 @@ void CPUParticles2D::_particles_process(double p_delta) { if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { - p.transform.elements[1] = p.velocity.normalized(); - p.transform.elements[0] = p.transform.elements[1].orthogonal(); + p.transform.columns[1] = p.velocity.normalized(); + p.transform.columns[0] = p.transform.columns[1].orthogonal(); } } else { - p.transform.elements[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation)); - p.transform.elements[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation)); + p.transform.columns[0] = Vector2(Math::cos(p.rotation), -Math::sin(p.rotation)); + p.transform.columns[1] = Vector2(Math::sin(p.rotation), Math::cos(p.rotation)); } //scale by scale @@ -986,8 +986,8 @@ void CPUParticles2D::_particles_process(double p_delta) { if (base_scale.y < 0.00001) { base_scale.y = 0.00001; } - p.transform.elements[0] *= base_scale.x; - p.transform.elements[1] *= base_scale.y; + p.transform.columns[0] *= base_scale.x; + p.transform.columns[1] *= base_scale.y; p.transform[2] += p.velocity * local_delta; } @@ -1029,14 +1029,14 @@ void CPUParticles2D::_update_particle_data_buffer() { } if (r[idx].active) { - ptr[0] = t.elements[0][0]; - ptr[1] = t.elements[1][0]; + ptr[0] = t.columns[0][0]; + ptr[1] = t.columns[1][0]; ptr[2] = 0; - ptr[3] = t.elements[2][0]; - ptr[4] = t.elements[0][1]; - ptr[5] = t.elements[1][1]; + ptr[3] = t.columns[2][0]; + ptr[4] = t.columns[0][1]; + ptr[5] = t.columns[1][1]; ptr[6] = 0; - ptr[7] = t.elements[2][1]; + ptr[7] = t.columns[2][1]; } else { memset(ptr, 0, sizeof(float) * 8); @@ -1137,14 +1137,14 @@ void CPUParticles2D::_notification(int p_what) { Transform2D t = inv_emission_transform * r[i].transform; if (r[i].active) { - ptr[0] = t.elements[0][0]; - ptr[1] = t.elements[1][0]; + ptr[0] = t.columns[0][0]; + ptr[1] = t.columns[1][0]; ptr[2] = 0; - ptr[3] = t.elements[2][0]; - ptr[4] = t.elements[0][1]; - ptr[5] = t.elements[1][1]; + ptr[3] = t.columns[2][0]; + ptr[4] = t.columns[0][1]; + ptr[5] = t.columns[1][1]; ptr[6] = 0; - ptr[7] = t.elements[2][1]; + ptr[7] = t.columns[2][1]; } else { memset(ptr, 0, sizeof(float) * 8); diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h index 7ae51e3966..51d58723b4 100644 --- a/scene/2d/cpu_particles_2d.h +++ b/scene/2d/cpu_particles_2d.h @@ -137,7 +137,7 @@ private: real_t randomness_ratio = 0.0; double lifetime_randomness = 0.0; double speed_scale = 1.0; - bool local_coords; + bool local_coords = false; int fixed_fps = 0; bool fractional_delta = true; diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index c69eeb52a8..d509d93172 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -114,8 +114,8 @@ void GPUParticles2D::set_use_local_coordinates(bool p_enable) { void GPUParticles2D::_update_particle_emission_transform() { Transform2D xf2d = get_global_transform(); Transform3D xf; - xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0)); - xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0)); + xf.basis.set_column(0, Vector3(xf2d.columns[0].x, xf2d.columns[0].y, 0)); + xf.basis.set_column(1, Vector3(xf2d.columns[1].x, xf2d.columns[1].y, 0)); xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0)); RS::get_singleton()->particles_set_emission_transform(particles, xf); @@ -287,6 +287,15 @@ bool GPUParticles2D::get_fractional_delta() const { return fractional_delta; } +void GPUParticles2D::set_interpolate(bool p_enable) { + interpolate = p_enable; + RS::get_singleton()->particles_set_interpolate(particles, p_enable); +} + +bool GPUParticles2D::get_interpolate() const { + return interpolate; +} + TypedArray<String> GPUParticles2D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); @@ -337,8 +346,8 @@ void GPUParticles2D::_validate_property(PropertyInfo &property) const { void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { Transform3D transform; - transform.basis.set_axis(0, Vector3(p_transform2d.get_axis(0).x, p_transform2d.get_axis(0).y, 0)); - transform.basis.set_axis(1, Vector3(p_transform2d.get_axis(1).x, p_transform2d.get_axis(1).y, 0)); + transform.basis.set_column(0, Vector3(p_transform2d.columns[0].x, p_transform2d.columns[0].y, 0)); + transform.basis.set_column(1, Vector3(p_transform2d.columns[1].x, p_transform2d.columns[1].y, 0)); transform.set_origin(Vector3(p_transform2d.get_origin().x, p_transform2d.get_origin().y, 0)); Vector3 velocity = Vector3(p_velocity2d.x, p_velocity2d.y, 0); @@ -543,6 +552,7 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles2D::set_use_local_coordinates); ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles2D::set_fixed_fps); ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles2D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_interpolate", "enable"), &GPUParticles2D::set_interpolate); ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles2D::set_process_material); ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles2D::set_speed_scale); ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles2D::set_collision_base_size); @@ -558,6 +568,7 @@ void GPUParticles2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles2D::get_use_local_coordinates); ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles2D::get_fixed_fps); ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles2D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_interpolate"), &GPUParticles2D::get_interpolate); ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles2D::get_process_material); ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles2D::get_speed_scale); ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles2D::get_collision_base_size); @@ -601,6 +612,7 @@ void GPUParticles2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); @@ -644,6 +656,7 @@ GPUParticles2D::GPUParticles2D() { set_lifetime(1); set_fixed_fps(0); set_fractional_delta(true); + set_interpolate(true); set_pre_process_time(0); set_explosiveness_ratio(0); set_randomness_ratio(0); @@ -653,9 +666,6 @@ GPUParticles2D::GPUParticles2D() { set_speed_scale(1); set_fixed_fps(30); set_collision_base_size(collision_base_size); -#ifdef TOOLS_ENABLED - show_visibility_rect = false; -#endif } GPUParticles2D::~GPUParticles2D() { diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index fc95ae27b2..3c7f4cd9b5 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -47,19 +47,20 @@ public: private: RID particles; - bool one_shot; - int amount; - double lifetime; - double pre_process_time; - real_t explosiveness_ratio; - real_t randomness_ratio; - double speed_scale; + bool one_shot = false; + int amount = 0; + double lifetime = 0.0; + double pre_process_time = 0.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; + double speed_scale = 0.0; Rect2 visibility_rect; - bool local_coords; - int fixed_fps; - bool fractional_delta; + bool local_coords = false; + int fixed_fps = 0; + bool fractional_delta = false; + bool interpolate = true; #ifdef TOOLS_ENABLED - bool show_visibility_rect; + bool show_visibility_rect = false; #endif Ref<Material> process_material; @@ -133,6 +134,9 @@ public: void set_fractional_delta(bool p_enable); bool get_fractional_delta() const; + void set_interpolate(bool p_enable); + bool get_interpolate() const; + void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index c4e57b375d..14188d7120 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -285,7 +285,6 @@ void LightOccluder2D::_bind_methods() { LightOccluder2D::LightOccluder2D() { occluder = RS::get_singleton()->canvas_light_occluder_create(); - mask = 1; set_notify_transform(true); set_as_sdf_collision(true); diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 4f8c6d20df..4acfeaf781 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -81,10 +81,9 @@ class LightOccluder2D : public Node2D { GDCLASS(LightOccluder2D, Node2D); RID occluder; - bool enabled; - int mask; + int mask = 1; Ref<OccluderPolygon2D> occluder_polygon; - bool sdf_collision; + bool sdf_collision = false; void _poly_changed(); protected: diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h index 2fb6fab91c..1447e25e8c 100644 --- a/scene/2d/navigation_agent_2d.h +++ b/scene/2d/navigation_agent_2d.h @@ -45,17 +45,17 @@ class NavigationAgent2D : public Node { uint32_t navigable_layers = 1; real_t target_desired_distance = 1.0; - real_t radius; - real_t neighbor_dist; - int max_neighbors; - real_t time_horizon; - real_t max_speed; + real_t radius = 0.0; + real_t neighbor_dist = 0.0; + int max_neighbors = 0; + real_t time_horizon = 0.0; + real_t max_speed = 0.0; real_t path_max_distance = 3.0; Vector2 target_location; Vector<Vector2> navigation_path; - int nav_path_index; + int nav_path_index = 0; bool velocity_submitted = false; Vector2 prev_safe_velocity; /// The submitted target velocity @@ -63,7 +63,7 @@ class NavigationAgent2D : public Node { bool target_reached = false; bool navigation_finished = true; // No initialized on purpose - uint32_t update_frame_id; + uint32_t update_frame_id = 0; protected: static void _bind_methods(); diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp index d1e5bc11bc..b594aa3bb2 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); @@ -79,7 +82,7 @@ void NavigationObstacle2D::_notification(int p_what) { } 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 +106,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 +130,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 +147,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_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 261d371dc4..f46453283c 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); @@ -387,6 +387,10 @@ uint32_t NavigationRegion2D::get_layers() const { return NavigationServer2D::get_singleton()->region_get_layers(region); } +RID NavigationRegion2D::get_region_rid() const { + return region; +} + ///////////////////////////// #ifdef TOOLS_ENABLED Rect2 NavigationRegion2D::_edit_get_rect() const { @@ -534,6 +538,8 @@ void NavigationRegion2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion2D::set_layers); ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion2D::get_layers); + ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion2D::get_region_rid); + ClassDB::bind_method(D_METHOD("_navpoly_changed"), &NavigationRegion2D::_navpoly_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navpoly", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon"); diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h index 487a578401..3c4a4e81d9 100644 --- a/scene/2d/navigation_region_2d.h +++ b/scene/2d/navigation_region_2d.h @@ -117,6 +117,8 @@ public: void set_layers(uint32_t p_layers); uint32_t get_layers() const; + RID get_region_rid() const; + void set_navigation_polygon(const Ref<NavigationPolygon> &p_navpoly); Ref<NavigationPolygon> get_navigation_polygon() const; diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index dd88bda304..0e8bb1aad7 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -111,7 +111,7 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { #endif void Node2D::_update_xform_values() { - position = transform.elements[2]; + position = transform.columns[2]; rotation = transform.get_rotation(); scale = transform.get_scale(); skew = transform.get_skew(); @@ -120,7 +120,7 @@ void Node2D::_update_xform_values() { void Node2D::_update_transform() { transform.set_rotation_scale_and_skew(rotation, scale, skew); - transform.elements[2] = position; + transform.columns[2] = position; RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform); @@ -133,7 +133,7 @@ void Node2D::_update_transform() { void Node2D::set_position(const Point2 &p_pos) { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } position = p_pos; _update_transform(); @@ -141,7 +141,7 @@ void Node2D::set_position(const Point2 &p_pos) { void Node2D::set_rotation(real_t p_radians) { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } rotation = p_radians; _update_transform(); @@ -149,7 +149,7 @@ void Node2D::set_rotation(real_t p_radians) { void Node2D::set_skew(real_t p_radians) { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } skew = p_radians; _update_transform(); @@ -157,7 +157,7 @@ void Node2D::set_skew(real_t p_radians) { void Node2D::set_scale(const Size2 &p_scale) { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } scale = p_scale; // Avoid having 0 scale values, can lead to errors in physics and rendering. @@ -172,14 +172,14 @@ void Node2D::set_scale(const Size2 &p_scale) { Point2 Node2D::get_position() const { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } return position; } real_t Node2D::get_rotation() const { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } return rotation; @@ -187,7 +187,7 @@ real_t Node2D::get_rotation() const { real_t Node2D::get_skew() const { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } return skew; @@ -195,7 +195,7 @@ real_t Node2D::get_skew() const { Size2 Node2D::get_scale() const { if (_xform_dirty) { - ((Node2D *)this)->_update_xform_values(); + const_cast<Node2D *>(this)->_update_xform_values(); } return scale; diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index dcbb6507f5..335f2404f2 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -163,15 +163,15 @@ Vector2 ParallaxBackground::get_final_offset() const { void ParallaxBackground::_bind_methods() { ClassDB::bind_method(D_METHOD("_camera_moved"), &ParallaxBackground::_camera_moved); - ClassDB::bind_method(D_METHOD("set_scroll_offset", "ofs"), &ParallaxBackground::set_scroll_offset); + ClassDB::bind_method(D_METHOD("set_scroll_offset", "offset"), &ParallaxBackground::set_scroll_offset); ClassDB::bind_method(D_METHOD("get_scroll_offset"), &ParallaxBackground::get_scroll_offset); - ClassDB::bind_method(D_METHOD("set_scroll_base_offset", "ofs"), &ParallaxBackground::set_scroll_base_offset); + ClassDB::bind_method(D_METHOD("set_scroll_base_offset", "offset"), &ParallaxBackground::set_scroll_base_offset); ClassDB::bind_method(D_METHOD("get_scroll_base_offset"), &ParallaxBackground::get_scroll_base_offset); ClassDB::bind_method(D_METHOD("set_scroll_base_scale", "scale"), &ParallaxBackground::set_scroll_base_scale); ClassDB::bind_method(D_METHOD("get_scroll_base_scale"), &ParallaxBackground::get_scroll_base_scale); - ClassDB::bind_method(D_METHOD("set_limit_begin", "ofs"), &ParallaxBackground::set_limit_begin); + ClassDB::bind_method(D_METHOD("set_limit_begin", "offset"), &ParallaxBackground::set_limit_begin); ClassDB::bind_method(D_METHOD("get_limit_begin"), &ParallaxBackground::get_limit_begin); - ClassDB::bind_method(D_METHOD("set_limit_end", "ofs"), &ParallaxBackground::set_limit_end); + ClassDB::bind_method(D_METHOD("set_limit_end", "offset"), &ParallaxBackground::set_limit_end); ClassDB::bind_method(D_METHOD("get_limit_end"), &ParallaxBackground::get_limit_end); ClassDB::bind_method(D_METHOD("set_ignore_camera_zoom", "ignore"), &ParallaxBackground::set_ignore_camera_zoom); ClassDB::bind_method(D_METHOD("is_ignore_camera_zoom"), &ParallaxBackground::is_ignore_camera_zoom); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index aa68349329..a21cb7db62 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -311,7 +311,7 @@ void PathFollow2D::set_offset(real_t p_offset) { if (path->get_curve().is_valid()) { real_t path_length = path->get_curve()->get_baked_length(); - if (loop) { + if (loop && path_length) { offset = Math::fposmod(offset, path_length); if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) { offset = path_length; diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h index 9f31c22031..22d329c320 100644 --- a/scene/2d/physical_bone_2d.h +++ b/scene/2d/physical_bone_2d.h @@ -49,7 +49,7 @@ private: NodePath bone2d_nodepath; bool follow_bone_when_simulating = false; - Joint2D *child_joint; + Joint2D *child_joint = nullptr; bool auto_configure_joint = true; bool simulate_physics = false; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index f96c7b512f..f345d8c3c9 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -58,7 +58,11 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_distance, bool p PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_distance, p_margin); PhysicsServer2D::MotionResult result; - if (move_and_collide(parameters, result, p_test_only)) { + + 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) { // 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(); @@ -119,7 +123,7 @@ bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_ if (!p_test_only) { Transform2D gt = p_parameters.from; - gt.elements[2] += r_result.travel; + gt.columns[2] += r_result.travel; set_global_transform(gt); } @@ -277,7 +281,7 @@ void AnimatableBody2D::_update_kinematic_motion() { } void AnimatableBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { - AnimatableBody2D *body = (AnimatableBody2D *)p_instance; + AnimatableBody2D *body = static_cast<AnimatableBody2D *>(p_instance); body->_body_state_changed(p_state); } @@ -335,17 +339,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; @@ -356,17 +360,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; @@ -380,45 +384,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)); @@ -427,7 +431,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); @@ -443,7 +447,7 @@ struct _RigidDynamicBody2DInOut { }; void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { - RigidDynamicBody2D *body = (RigidDynamicBody2D *)p_instance; + RigidDynamicBody2D *body = static_cast<RigidDynamicBody2D *>(p_instance); body->_body_state_changed(p_state); } @@ -490,7 +494,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; @@ -501,7 +505,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; @@ -511,7 +515,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 @@ -924,7 +928,7 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); - if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) { + if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) { warnings.push_back(RTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } @@ -1106,7 +1110,7 @@ bool CharacterBody2D::move_and_slide() { Vector2 current_platform_velocity = platform_velocity; Transform2D gt = get_global_transform(); - previous_position = gt.elements[2]; + previous_position = gt.columns[2]; if ((on_floor || on_wall) && platform_rid.is_valid()) { bool excluded = false; @@ -1119,7 +1123,7 @@ bool CharacterBody2D::move_and_slide() { //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); if (bs) { - Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2]; + Vector2 local_position = gt.columns[2] - bs->get_transform().columns[2]; current_platform_velocity = bs->get_velocity_at_local_position(local_position); } else { // Body is removed or destroyed, invalidate floor. @@ -1200,7 +1204,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); - Vector2 prev_position = parameters.from.elements[2]; + Vector2 prev_position = parameters.from.columns[2]; PhysicsServer2D::MotionResult result; bool collided = move_and_collide(parameters, result, false, !sliding_enabled); @@ -1227,7 +1231,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (on_floor && floor_stop_on_slope && (velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); if (result.travel.length() <= margin + CMP_EPSILON) { - gt.elements[2] -= result.travel; + gt.columns[2] -= result.travel; } set_global_transform(gt); velocity = Vector2(); @@ -1249,11 +1253,11 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (result.travel.length() <= margin + CMP_EPSILON) { // Cancels the motion. Transform2D gt = get_global_transform(); - gt.elements[2] -= result.travel; + gt.columns[2] -= result.travel; set_global_transform(gt); } // Determines if you are on the ground. - _snap_on_floor(true, false); + _snap_on_floor(true, false, true); velocity = Vector2(); last_motion = Vector2(); motion = Vector2(); @@ -1308,7 +1312,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo can_apply_constant_speed = false; sliding_enabled = true; Transform2D gt = get_global_transform(); - gt.elements[2] = prev_position; + gt.columns[2] = prev_position; set_global_transform(gt); Vector2 motion_slide_norm = motion.slide(prev_floor_normal).normalized(); @@ -1392,8 +1396,8 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) { } } -void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (on_floor || !was_on_floor || vel_dir_facing_up) { +void CharacterBody2D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor) { + if (on_floor || !p_was_on_floor || p_vel_dir_facing_up) { return; } @@ -1405,7 +1409,8 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) PhysicsServer2D::MotionResult result; if (move_and_collide(parameters, result, true, false)) { - if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + if ((result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) || + (p_wall_as_floor && result.get_angle(-up_direction) > floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { on_floor = true; floor_normal = result.collision_normal; _set_platform_data(result); @@ -1420,14 +1425,14 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } } - parameters.from.elements[2] += result.travel; + parameters.from.columns[2] += result.travel; set_global_transform(parameters.from); } } } -bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { +bool CharacterBody2D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) { + if (up_direction == Vector2() || on_floor || !p_was_on_floor || p_vel_dir_facing_up) { return false; } @@ -1516,7 +1521,7 @@ const Vector2 &CharacterBody2D::get_last_motion() const { } Vector2 CharacterBody2D::get_position_delta() const { - return get_global_transform().elements[2] - previous_position; + return get_global_transform().columns[2] - previous_position; } const Vector2 &CharacterBody2D::get_real_velocity() const { diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 8d9e31d4dd..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; @@ -441,11 +441,11 @@ private: Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); Ref<KinematicCollision2D> _get_last_slide_collision(); const Vector2 &get_up_direction() const; - bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up); + bool _on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up); void set_up_direction(const Vector2 &p_up_direction); void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result); void _set_platform_data(const PhysicsServer2D::MotionResult &p_result); - void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up); + void _snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up, bool p_wall_as_floor = false); protected: void _notification(int p_what); diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index 518593cee1..d946ea63bf 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -30,8 +30,6 @@ #include "position_2d.h" -const real_t DEFAULT_GIZMO_EXTENTS = 10.0; - void Position2D::_draw_cross() { const real_t extents = get_gizmo_extents(); @@ -103,28 +101,19 @@ void Position2D::_notification(int p_what) { } void Position2D::set_gizmo_extents(real_t p_extents) { - if (p_extents == DEFAULT_GIZMO_EXTENTS) { - set_meta("_gizmo_extents_", Variant()); - } else { - set_meta("_gizmo_extents_", p_extents); - } - + gizmo_extents = p_extents; update(); } real_t Position2D::get_gizmo_extents() const { - if (has_meta("_gizmo_extents_")) { - return get_meta("_gizmo_extents_"); - } else { - return DEFAULT_GIZMO_EXTENTS; - } + return gizmo_extents; } void Position2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents); - ClassDB::bind_method(D_METHOD("_get_gizmo_extents"), &Position2D::get_gizmo_extents); + ClassDB::bind_method(D_METHOD("set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents); + ClassDB::bind_method(D_METHOD("get_gizmo_extents"), &Position2D::get_gizmo_extents); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater"), "set_gizmo_extents", "get_gizmo_extents"); } Position2D::Position2D() { diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index 4ef07eb05c..99b0266130 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -36,6 +36,8 @@ class Position2D : public Node2D { GDCLASS(Position2D, Node2D); + real_t gizmo_extents = 10.0; + void _draw_cross(); protected: 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/tile_map.cpp b/scene/2d/tile_map.cpp index cbbadf1178..19c341c1e1 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,7 +810,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) { Vector2i pk = E->get(); Vector2i pk_world_coords = map_to_world(pk); q->self()->map_to_world[pk] = pk_world_coords; @@ -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,7 +1250,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); TileSetSource *source; @@ -1462,7 +1464,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { TileMapCell c = get_cell(q.layer, E_cell->get(), true); TileSetSource *source; @@ -1659,7 +1661,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { TileMapCell c = get_cell(q.layer, E_cell->get(), true); TileSetSource *source; @@ -1748,7 +1750,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true); TileSetSource *source; @@ -1823,7 +1825,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { const TileMapCell &c = get_cell(q.layer, E_cell->get(), true); TileSetSource *source; @@ -1881,7 +1883,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell->get(), true); TileSetSource *source; @@ -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) { @@ -2143,17 +2145,17 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern); - set_cell(p_layer, coords, p_pattern->get_cell_source_id(coords), p_pattern->get_cell_atlas_coords(coords), p_pattern->get_cell_alternative_tile(coords)); + set_cell(p_layer, coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i])); } } -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,17 +2179,17 @@ 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 (RBSet<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) { 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)) { @@ -2197,14 +2199,14 @@ Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_ce } // 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()) { + RBSet<TerrainConstraint> constraints; + for (RBSet<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) { TerrainConstraint c = E->get(); - 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,8 +2347,8 @@ 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()) { + RBSet<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern); + for (RBSet<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) { constraints.insert(E_constraint->get()); } @@ -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,15 +2417,15 @@ 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()) { + for (RBSet<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); } } @@ -2512,10 +2514,10 @@ void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) { uint32_t v = decode_uint32(&local[4]); // Extract the transform flags that used to be in the tilemap. - bool flip_h = v & (1 << 29); - bool flip_v = v & (1 << 30); - bool transpose = v & (1 << 31); - v &= (1 << 29) - 1; + bool flip_h = v & (1UL << 29); + bool flip_v = v & (1UL << 30); + bool transpose = v & (1UL << 31); + v &= (1UL << 29) - 1; // Extract autotile/atlas coords. int16_t coord_x = 0; @@ -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,7 +3524,7 @@ 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()) { + for (RBSet<Vector2i>::Element *E = p_cells.front(); E; E = E->next()) { Vector2 center = map_to_world(E->get()); #define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \ @@ -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/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 9a68c17269..4a4a2a1da0 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -190,7 +190,7 @@ String TouchScreenButton::get_action() const { void TouchScreenButton::input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (!get_tree()) { + if (!is_visible_in_tree()) { return; } @@ -198,8 +198,6 @@ void TouchScreenButton::input(const Ref<InputEvent> &p_event) { return; } - ERR_FAIL_COND(!is_visible_in_tree()); - const InputEventScreenTouch *st = Object::cast_to<InputEventScreenTouch>(*p_event); if (passby_press) { 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 78c968a3d4..54737e13b3 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -175,7 +175,7 @@ void Area3D::_initialize_wind() { Node3D *p_wind_source = Object::cast_to<Node3D>(get_node(wind_source_path)); ERR_FAIL_NULL(p_wind_source); Transform3D global_transform = p_wind_source->get_transform(); - wind_direction = -global_transform.basis.get_axis(Vector3::AXIS_Z).normalized(); + wind_direction = -global_transform.basis.get_column(Vector3::AXIS_Z).normalized(); wind_source = global_transform.origin; temp_magnitude = wind_force_magnitude; } @@ -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 c2399985ff..3b892baf57 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -49,7 +49,7 @@ public: private: SpaceOverride gravity_space_override = SPACE_OVERRIDE_DISABLED; Vector3 gravity_vec; - real_t gravity; + real_t gravity = 0.0; bool gravity_is_point = false; real_t gravity_distance_scale = 0.0; @@ -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; @@ -228,4 +228,4 @@ public: VARIANT_ENUM_CAST(Area3D::SpaceOverride); -#endif // AREA__H +#endif // AREA_3D_H diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index b17201f86b..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()); @@ -447,7 +447,7 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { if (emission_angle_enabled) { Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; - float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative + float c = listenertopos.normalized().dot(get_global_transform().basis.get_column(2).normalized()); //it's z negative float angle = Math::rad2deg(Math::acos(c)); if (angle > emission_angle) { db_att -= -emission_angle_filter_attenuation_db; @@ -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/camera_3d.cpp b/scene/3d/camera_3d.cpp index 4eace17cc0..4c53776bba 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -144,8 +144,8 @@ void Camera3D::_notification(int p_what) { Transform3D Camera3D::get_camera_transform() const { Transform3D tr = get_global_transform().orthonormalized(); - tr.origin += tr.basis.get_axis(1) * v_offset; - tr.origin += tr.basis.get_axis(0) * h_offset; + tr.origin += tr.basis.get_column(1) * v_offset; + tr.origin += tr.basis.get_column(0) * h_offset; return tr; } @@ -217,8 +217,6 @@ void Camera3D::make_current() { } get_viewport()->_camera_3d_set(this); - - //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); } void Camera3D::clear_current(bool p_enable_next) { @@ -307,7 +305,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { bool Camera3D::is_position_behind(const Vector3 &p_pos) const { Transform3D t = get_global_transform(); - Vector3 eyedir = -t.basis.get_axis(2).normalized(); + Vector3 eyedir = -t.basis.get_column(2).normalized(); return eyedir.dot(p_pos - t.origin) < near; } @@ -475,9 +473,9 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near); ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection); ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection); - ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset); + ClassDB::bind_method(D_METHOD("set_h_offset", "offset"), &Camera3D::set_h_offset); ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset); - ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera3D::set_v_offset); + ClassDB::bind_method(D_METHOD("set_v_offset", "offset"), &Camera3D::set_v_offset); ClassDB::bind_method(D_METHOD("get_v_offset"), &Camera3D::get_v_offset); ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &Camera3D::set_cull_mask); ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask); @@ -509,7 +507,7 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.001,16384,0.001"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far"); @@ -557,7 +555,7 @@ void Camera3D::set_fov(real_t p_fov) { } void Camera3D::set_size(real_t p_size) { - ERR_FAIL_COND(p_size < 0.1 || p_size > 16384); + ERR_FAIL_COND(p_size < 0.001 || p_size > 16384); size = p_size; _update_camera_mode(); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 40c09593a4..70cab77eda 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -345,7 +345,7 @@ 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()) { + for (RBSet<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()]; ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); @@ -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/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index d28e11a2e9..759997de7b 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -110,7 +110,7 @@ void CollisionShape3D::_notification(int p_what) { } } -void CollisionShape3D::resource_changed(RES res) { +void CollisionShape3D::resource_changed(Ref<Resource> res) { update_gizmos(); } diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index fbcabf6529..5c32230942 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -43,7 +43,7 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *parent = nullptr; - void resource_changed(RES res); + void resource_changed(Ref<Resource> res); bool disabled = false; protected: diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 0befda4168..9666994b95 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -833,8 +833,8 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 normal = emission_normals.get(random_idx); Vector2 normal_2d(normal.x, normal.y); Transform2D m2; - m2.set_axis(0, normal_2d); - m2.set_axis(1, normal_2d.orthogonal()); + m2.columns[0] = normal_2d; + m2.columns[1] = normal_2d.orthogonal(); Vector2 velocity_2d(p.velocity.x, p.velocity.y); velocity_2d = m2.basis_xform(velocity_2d); p.velocity.x = velocity_2d.x; @@ -845,9 +845,9 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 tangent = v0.cross(normal).normalized(); Vector3 bitangent = tangent.cross(normal).normalized(); Basis m3; - m3.set_axis(0, tangent); - m3.set_axis(1, bitangent); - m3.set_axis(2, normal); + m3.set_column(0, tangent); + m3.set_column(1, bitangent); + m3.set_column(2, normal); p.velocity = m3.xform(p.velocity); } } @@ -1068,33 +1068,33 @@ void CPUParticles3D::_particles_process(double p_delta) { if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { - p.transform.basis.set_axis(1, p.velocity.normalized()); + p.transform.basis.set_column(1, p.velocity.normalized()); } else { - p.transform.basis.set_axis(1, p.transform.basis.get_axis(1)); + p.transform.basis.set_column(1, p.transform.basis.get_column(1)); } - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); - p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); + p.transform.basis.set_column(2, Vector3(0, 0, 1)); } else { - p.transform.basis.set_axis(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0)); - p.transform.basis.set_axis(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0)); - p.transform.basis.set_axis(2, Vector3(0, 0, 1)); + p.transform.basis.set_column(0, Vector3(Math::cos(p.custom[0]), -Math::sin(p.custom[0]), 0.0)); + p.transform.basis.set_column(1, Vector3(Math::sin(p.custom[0]), Math::cos(p.custom[0]), 0.0)); + p.transform.basis.set_column(2, Vector3(0, 0, 1)); } } else { //orient particle Y towards velocity if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { - p.transform.basis.set_axis(1, p.velocity.normalized()); + p.transform.basis.set_column(1, p.velocity.normalized()); } else { - p.transform.basis.set_axis(1, p.transform.basis.get_axis(1).normalized()); + p.transform.basis.set_column(1, p.transform.basis.get_column(1).normalized()); } - if (p.transform.basis.get_axis(1) == p.transform.basis.get_axis(0)) { - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); - p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); + if (p.transform.basis.get_column(1) == p.transform.basis.get_column(0)) { + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); + p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized()); } else { - p.transform.basis.set_axis(2, p.transform.basis.get_axis(0).cross(p.transform.basis.get_axis(1)).normalized()); - p.transform.basis.set_axis(0, p.transform.basis.get_axis(1).cross(p.transform.basis.get_axis(2)).normalized()); + p.transform.basis.set_column(2, p.transform.basis.get_column(0).cross(p.transform.basis.get_column(1)).normalized()); + p.transform.basis.set_column(0, p.transform.basis.get_column(1).cross(p.transform.basis.get_column(2)).normalized()); } } else { p.transform.basis.orthonormalize(); @@ -1159,7 +1159,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ERR_FAIL_NULL(get_viewport()); Camera3D *c = get_viewport()->get_camera_3d(); if (c) { - Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close + Vector3 dir = c->get_global_transform().basis.get_column(2); //far away to close if (local_coords) { // will look different from Particles in editor as this is based on the camera in the scenetree @@ -1187,17 +1187,17 @@ void CPUParticles3D::_update_particle_data_buffer() { } if (r[idx].active) { - ptr[0] = t.basis.elements[0][0]; - ptr[1] = t.basis.elements[0][1]; - ptr[2] = t.basis.elements[0][2]; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; ptr[3] = t.origin.x; - ptr[4] = t.basis.elements[1][0]; - ptr[5] = t.basis.elements[1][1]; - ptr[6] = t.basis.elements[1][2]; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; ptr[7] = t.origin.y; - ptr[8] = t.basis.elements[2][0]; - ptr[9] = t.basis.elements[2][1]; - ptr[10] = t.basis.elements[2][2]; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; ptr[11] = t.origin.z; } else { memset(ptr, 0, sizeof(Transform3D)); @@ -1293,17 +1293,17 @@ void CPUParticles3D::_notification(int p_what) { Transform3D t = inv_emission_transform * r[i].transform; if (r[i].active) { - ptr[0] = t.basis.elements[0][0]; - ptr[1] = t.basis.elements[0][1]; - ptr[2] = t.basis.elements[0][2]; + ptr[0] = t.basis.rows[0][0]; + ptr[1] = t.basis.rows[0][1]; + ptr[2] = t.basis.rows[0][2]; ptr[3] = t.origin.x; - ptr[4] = t.basis.elements[1][0]; - ptr[5] = t.basis.elements[1][1]; - ptr[6] = t.basis.elements[1][2]; + ptr[4] = t.basis.rows[1][0]; + ptr[5] = t.basis.rows[1][1]; + ptr[6] = t.basis.rows[1][2]; ptr[7] = t.origin.y; - ptr[8] = t.basis.elements[2][0]; - ptr[9] = t.basis.elements[2][1]; - ptr[10] = t.basis.elements[2][2]; + ptr[8] = t.basis.rows[2][0]; + ptr[9] = t.basis.rows[2][1]; + ptr[10] = t.basis.rows[2][2]; ptr[11] = t.origin.z; } else { memset(ptr, 0, sizeof(float) * 12); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index 521b6c615e..7f225ee98d 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -174,9 +174,9 @@ private: Vector<Color> emission_colors; int emission_point_count = 0; Vector3 emission_ring_axis; - real_t emission_ring_height; - real_t emission_ring_radius; - real_t emission_ring_inner_radius; + real_t emission_ring_height = 0.0; + real_t emission_ring_radius = 0.0; + real_t emission_ring_inner_radius = 0.0; Ref<Curve> scale_curve_x; Ref<Curve> scale_curve_y; diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index f3eb52d124..adce45a0a9 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -61,16 +61,16 @@ private: RID particles; bool one_shot; - int amount; - double lifetime; - double pre_process_time; - real_t explosiveness_ratio; - real_t randomness_ratio; - double speed_scale; + int amount = 0; + double lifetime = 0.0; + double pre_process_time = 0.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; + double speed_scale = 0.0; AABB visibility_aabb; - bool local_coords; - int fixed_fps; - bool fractional_delta; + bool local_coords = false; + int fixed_fps = 0; + bool fractional_delta = false; bool interpolate = true; NodePath sub_emitter; real_t collision_base_size = 0.01; diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 6f94df284a..4552ed9537 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -594,8 +594,8 @@ void GPUParticlesCollisionHeightField3D::_notification(int p_what) { Camera3D *cam = get_viewport()->get_camera_3d(); if (cam) { Transform3D xform = get_global_transform(); - Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized(); - Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 x_axis = xform.basis.get_column(Vector3::AXIS_X).normalized(); + Vector3 z_axis = xform.basis.get_column(Vector3::AXIS_Z).normalized(); float x_len = xform.basis.get_scale().x; float z_len = xform.basis.get_scale().z; @@ -640,18 +640,13 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField3D::set_update_mode); ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField3D::get_update_mode); - ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_mode); - ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled); - - ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio); - ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio); + ClassDB::bind_method(D_METHOD("set_follow_camera_enabled", "enabled"), &GPUParticlesCollisionHeightField3D::set_follow_camera_enabled); + ClassDB::bind_method(D_METHOD("is_follow_camera_enabled"), &GPUParticlesCollisionHeightField3D::is_follow_camera_enabled); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode"); - ADD_GROUP("Follow Camera", "follow_camera_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256 (Fastest),512 (Fast),1024 (Average),2048 (Slow),4096 (Slower),8192 (Slowest)"), "set_resolution", "get_resolution"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "When Moved (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_enabled", "is_follow_camera_enabled"); BIND_ENUM_CONSTANT(RESOLUTION_256); BIND_ENUM_CONSTANT(RESOLUTION_512); @@ -665,14 +660,6 @@ void GPUParticlesCollisionHeightField3D::_bind_methods() { BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS); } -void GPUParticlesCollisionHeightField3D::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) { - follow_camera_push_ratio = p_follow_camera_push_ratio; -} - -float GPUParticlesCollisionHeightField3D::get_follow_camera_push_ratio() const { - return follow_camera_push_ratio; -} - void GPUParticlesCollisionHeightField3D::set_extents(const Vector3 &p_extents) { extents = p_extents; RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); @@ -704,12 +691,12 @@ GPUParticlesCollisionHeightField3D::UpdateMode GPUParticlesCollisionHeightField3 return update_mode; } -void GPUParticlesCollisionHeightField3D::set_follow_camera_mode(bool p_enabled) { +void GPUParticlesCollisionHeightField3D::set_follow_camera_enabled(bool p_enabled) { follow_camera_mode = p_enabled; set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); } -bool GPUParticlesCollisionHeightField3D::is_follow_camera_mode_enabled() const { +bool GPUParticlesCollisionHeightField3D::is_follow_camera_enabled() const { return follow_camera_mode; } diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index fdd2fa4b18..4b2cb930fa 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -211,7 +211,6 @@ private: Vector3 extents = Vector3(1, 1, 1); Resolution resolution = RESOLUTION_1024; bool follow_camera_mode = false; - float follow_camera_push_ratio = 0.1; UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED; @@ -229,11 +228,8 @@ public: void set_update_mode(UpdateMode p_update_mode); UpdateMode get_update_mode() const; - void set_follow_camera_mode(bool p_enabled); - bool is_follow_camera_mode_enabled() const; - - void set_follow_camera_push_ratio(float p_ratio); - float get_follow_camera_push_ratio() const; + void set_follow_camera_enabled(bool p_enabled); + bool is_follow_camera_enabled() const; virtual AABB get_aabb() const override; diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp new file mode 100644 index 0000000000..2d7da48ab1 --- /dev/null +++ b/scene/3d/label_3d.cpp @@ -0,0 +1,1010 @@ +/*************************************************************************/ +/* label_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "label_3d.h" + +#include "core/core_string_names.h" +#include "scene/resources/theme.h" +#include "scene/scene_string_names.h" + +void Label3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label3D::get_horizontal_alignment); + + ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label3D::set_vertical_alignment); + ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label3D::get_vertical_alignment); + + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &Label3D::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &Label3D::get_modulate); + + ClassDB::bind_method(D_METHOD("set_outline_modulate", "modulate"), &Label3D::set_outline_modulate); + ClassDB::bind_method(D_METHOD("get_outline_modulate"), &Label3D::get_outline_modulate); + + ClassDB::bind_method(D_METHOD("set_text", "text"), &Label3D::set_text); + ClassDB::bind_method(D_METHOD("get_text"), &Label3D::get_text); + + ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label3D::set_text_direction); + ClassDB::bind_method(D_METHOD("get_text_direction"), &Label3D::get_text_direction); + + ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label3D::set_opentype_feature); + ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label3D::get_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label3D::clear_opentype_features); + + ClassDB::bind_method(D_METHOD("set_language", "language"), &Label3D::set_language); + ClassDB::bind_method(D_METHOD("get_language"), &Label3D::get_language); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &Label3D::set_structured_text_bidi_override); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override"), &Label3D::get_structured_text_bidi_override); + + ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label3D::set_structured_text_bidi_override_options); + ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label3D::get_structured_text_bidi_override_options); + + ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label3D::set_uppercase); + ClassDB::bind_method(D_METHOD("is_uppercase"), &Label3D::is_uppercase); + + ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Label3D::set_render_priority); + ClassDB::bind_method(D_METHOD("get_render_priority"), &Label3D::get_render_priority); + + ClassDB::bind_method(D_METHOD("set_outline_render_priority", "priority"), &Label3D::set_outline_render_priority); + ClassDB::bind_method(D_METHOD("get_outline_render_priority"), &Label3D::get_outline_render_priority); + + ClassDB::bind_method(D_METHOD("set_font", "font"), &Label3D::set_font); + ClassDB::bind_method(D_METHOD("get_font"), &Label3D::get_font); + + ClassDB::bind_method(D_METHOD("set_font_size", "size"), &Label3D::set_font_size); + ClassDB::bind_method(D_METHOD("get_font_size"), &Label3D::get_font_size); + + ClassDB::bind_method(D_METHOD("set_outline_size", "outline_size"), &Label3D::set_outline_size); + ClassDB::bind_method(D_METHOD("get_outline_size"), &Label3D::get_outline_size); + + ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &Label3D::set_line_spacing); + ClassDB::bind_method(D_METHOD("get_line_spacing"), &Label3D::get_line_spacing); + + ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label3D::set_autowrap_mode); + ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label3D::get_autowrap_mode); + + ClassDB::bind_method(D_METHOD("set_width", "width"), &Label3D::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &Label3D::get_width); + + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &Label3D::set_pixel_size); + ClassDB::bind_method(D_METHOD("get_pixel_size"), &Label3D::get_pixel_size); + + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Label3D::set_offset); + ClassDB::bind_method(D_METHOD("get_offset"), &Label3D::get_offset); + + ClassDB::bind_method(D_METHOD("set_draw_flag", "flag", "enabled"), &Label3D::set_draw_flag); + ClassDB::bind_method(D_METHOD("get_draw_flag", "flag"), &Label3D::get_draw_flag); + + ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &Label3D::set_billboard_mode); + ClassDB::bind_method(D_METHOD("get_billboard_mode"), &Label3D::get_billboard_mode); + + ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &Label3D::set_alpha_cut_mode); + ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &Label3D::get_alpha_cut_mode); + + ClassDB::bind_method(D_METHOD("set_alpha_scissor_threshold", "threshold"), &Label3D::set_alpha_scissor_threshold); + ClassDB::bind_method(D_METHOD("get_alpha_scissor_threshold"), &Label3D::get_alpha_scissor_threshold); + + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &Label3D::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &Label3D::get_texture_filter); + + ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &Label3D::generate_triangle_mesh); + + ClassDB::bind_method(D_METHOD("_queue_update"), &Label3D::_queue_update); + ClassDB::bind_method(D_METHOD("_font_changed"), &Label3D::_font_changed); + ClassDB::bind_method(D_METHOD("_im_update"), &Label3D::_im_update); + + 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::VECTOR2, "offset"), "set_offset", "get_offset"); + + ADD_GROUP("Flags", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority"); + + ADD_GROUP("Text", ""); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate"), "set_outline_modulate", "get_outline_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_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, "outline_size", PROPERTY_HINT_RANGE, "0,127,1"), "set_outline_size", "get_outline_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::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing"), "set_line_spacing", "get_line_spacing"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width"); + 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("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"); + + BIND_ENUM_CONSTANT(AUTOWRAP_OFF); + BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY); + BIND_ENUM_CONSTANT(AUTOWRAP_WORD); + BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART); + + BIND_ENUM_CONSTANT(FLAG_SHADED); + BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); + BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); + BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); + BIND_ENUM_CONSTANT(FLAG_MAX); + + BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); + BIND_ENUM_CONSTANT(ALPHA_CUT_DISCARD); + BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); +} + +bool Label3D::_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; + _queue_update(); + } + } else { + if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { + opentype_features[tag] = value; + dirty_font = true; + _queue_update(); + } + } + notify_property_list_changed(); + return true; + } + + return false; +} + +bool Label3D::_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 Label3D::_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)); +} + +void Label3D::_validate_property(PropertyInfo &property) const { + if (property.name == "material_override" || property.name == "material_overlay") { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + +void Label3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (!pending_update) { + _im_update(); + } + } break; + case NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = tr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + dirty_text = true; + _queue_update(); + } break; + } +} + +void Label3D::_im_update() { + _shape(); + + triangle_mesh.unref(); + update_gizmos(); + + pending_update = false; +} + +void Label3D::_queue_update() { + if (pending_update) { + return; + } + + pending_update = true; + call_deferred(SceneStringNames::get_singleton()->_im_update); +} + +AABB Label3D::get_aabb() const { + return aabb; +} + +Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { + if (triangle_mesh.is_valid()) { + return triangle_mesh; + } + + Ref<Font> font = _get_font_or_default(); + if (font.is_null()) { + return Ref<TriangleMesh>(); + } + + Vector<Vector3> faces; + faces.resize(6); + Vector3 *facesw = faces.ptrw(); + + float total_h = 0.0; + float max_line_w = 0.0; + for (int i = 0; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + max_line_w = MAX(max_line_w, TS->shaped_text_get_width(lines_rid[i])); + } + + float vbegin = 0; + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_FILL: + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (total_h - line_spacing) / 2.0; + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = (total_h - line_spacing); + } break; + } + + Vector2 offset = Vector2(0, vbegin); + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_LEFT: + break; + case HORIZONTAL_ALIGNMENT_FILL: + case HORIZONTAL_ALIGNMENT_CENTER: { + offset.x = -max_line_w / 2.0; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + offset.x = -max_line_w; + } break; + } + + Rect2 final_rect = Rect2(offset + lbl_offset, Size2(max_line_w, total_h)); + + if (final_rect.size.x == 0 || final_rect.size.y == 0) { + return Ref<TriangleMesh>(); + } + + real_t pixel_size = get_pixel_size(); + + Vector2 vertices[4] = { + + (final_rect.position + Vector2(0, -final_rect.size.y)) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, -final_rect.size.y)) * pixel_size, + (final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size, + final_rect.position * pixel_size, + + }; + + static const int indices[6] = { + 0, 1, 2, + 0, 2, 3 + }; + + for (int j = 0; j < 6; j++) { + int i = indices[j]; + Vector3 vtx; + vtx[0] = vertices[i][0]; + vtx[1] = vertices[i][1]; + facesw[j] = vtx; + } + + triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh)); + triangle_mesh->create(faces); + + return triangle_mesh; +} + +void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority, int p_outline_size) { + for (int j = 0; j < p_glyph.repeat; j++) { + Vector2 gl_of; + Vector2 gl_sz; + Rect2 gl_uv; + Size2 texs; + RID tex; + + if (p_glyph.font_rid != RID()) { + tex = TS->font_get_glyph_texture_rid(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + if (tex != RID()) { + gl_of = (TS->font_get_glyph_offset(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) + Vector2(p_glyph.x_off, p_glyph.y_off)) * pixel_size; + gl_sz = TS->font_get_glyph_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index) * pixel_size; + gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); + } + } else { + gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size; + gl_of = Vector2(0, -gl_sz.y); + } + + bool msdf = TS->font_is_multichannel_signed_distance_field(p_glyph.font_rid); + + uint64_t mat_hash; + if (tex != RID()) { + mat_hash = hash_one_uint64(tex.get_id()); + } else { + mat_hash = hash_one_uint64(0); + } + mat_hash = hash_djb2_one_64(p_priority | (p_outline_size << 31), mat_hash); + + if (!surfaces.has(mat_hash)) { + SurfaceData surf; + surf.material = RenderingServer::get_singleton()->material_create(); + // Set defaults for material, names need to match up those in StandardMaterial3D + RS::get_singleton()->material_set_param(surf.material, "albedo", Color(1, 1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "specular", 0.5); + RS::get_singleton()->material_set_param(surf.material, "metallic", 0.0); + RS::get_singleton()->material_set_param(surf.material, "roughness", 1.0); + RS::get_singleton()->material_set_param(surf.material, "uv1_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(surf.material, "uv1_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "uv2_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold); + if (msdf) { + RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range", TS->font_get_msdf_pixel_range(p_glyph.font_rid)); + RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size", p_outline_size); + } + + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), true, get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, &shader_rid); + + RS::get_singleton()->material_set_shader(surf.material, shader_rid); + RS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex); + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(surf.material, p_priority); + } else { + surf.z_shift = p_priority * pixel_size; + } + + surfaces[mat_hash] = surf; + } + SurfaceData &s = surfaces[mat_hash]; + + s.mesh_vertices.resize((s.offset + 1) * 4); + s.mesh_normals.resize((s.offset + 1) * 4); + s.mesh_tangents.resize((s.offset + 1) * 16); + s.mesh_colors.resize((s.offset + 1) * 4); + s.mesh_uvs.resize((s.offset + 1) * 4); + + s.mesh_vertices.write[(s.offset * 4) + 3] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 2] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y - gl_sz.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 1] = Vector3(r_offset.x + gl_of.x + gl_sz.x, r_offset.y - gl_of.y, s.z_shift); + s.mesh_vertices.write[(s.offset * 4) + 0] = Vector3(r_offset.x + gl_of.x, r_offset.y - gl_of.y, s.z_shift); + + for (int i = 0; i < 4; i++) { + s.mesh_normals.write[(s.offset * 4) + i] = Vector3(0.0, 0.0, 1.0); + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 0] = 1.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 1] = 0.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 2] = 0.0; + s.mesh_tangents.write[(s.offset * 16) + (i * 4) + 3] = 1.0; + s.mesh_colors.write[(s.offset * 4) + i] = p_modulate; + s.mesh_uvs.write[(s.offset * 4) + i] = Vector2(); + + if (aabb == AABB()) { + aabb.position = s.mesh_vertices[(s.offset * 4) + i]; + } else { + aabb.expand_to(s.mesh_vertices[(s.offset * 4) + i]); + } + } + + if (tex != RID()) { + s.mesh_uvs.write[(s.offset * 4) + 3] = Vector2(gl_uv.position.x / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 2] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, (gl_uv.position.y + gl_uv.size.y) / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 1] = Vector2((gl_uv.position.x + gl_uv.size.x) / texs.x, gl_uv.position.y / texs.y); + s.mesh_uvs.write[(s.offset * 4) + 0] = Vector2(gl_uv.position.x / texs.x, gl_uv.position.y / texs.y); + } + + s.indices.resize((s.offset + 1) * 6); + s.indices.write[(s.offset * 6) + 0] = (s.offset * 4) + 0; + s.indices.write[(s.offset * 6) + 1] = (s.offset * 4) + 1; + s.indices.write[(s.offset * 6) + 2] = (s.offset * 4) + 2; + s.indices.write[(s.offset * 6) + 3] = (s.offset * 4) + 0; + s.indices.write[(s.offset * 6) + 4] = (s.offset * 4) + 2; + s.indices.write[(s.offset * 6) + 5] = (s.offset * 4) + 3; + + s.offset++; + r_offset.x += p_glyph.advance * pixel_size; + } +} + +void Label3D::_shape() { + // Clear mesh. + RS::get_singleton()->mesh_clear(mesh); + aabb = AABB(); + + // Clear materials. + for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) { + RenderingServer::get_singleton()->free(E.value.material); + } + surfaces.clear(); + + Ref<Font> font = _get_font_or_default(); + ERR_FAIL_COND(font.is_null()); + + // 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; + dirty_lines = true; + } 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; + dirty_lines = true; + } + + if (dirty_lines) { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + + uint16_t autowrap_flags = TextServer::BREAK_MANDATORY; + switch (autowrap_mode) { + case AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_WORD: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_ARBITRARY: + autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; + break; + case AUTOWRAP_OFF: + break; + } + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + + float max_line_w = 0.0; + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); + max_line_w = MAX(max_line_w, TS->shaped_text_get_width(line)); + lines_rid.push_back(line); + } + + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { + for (int i = 0; i < lines_rid.size() - 1; i++) { + TS->shaped_text_fit_to_width(lines_rid[i], (width > 0) ? width : max_line_w, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA); + } + } + dirty_lines = false; + } + + // Generate surfaces and materials. + float total_h = 0.0; + for (int i = 0; i < lines_rid.size(); i++) { + total_h += (TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing) * pixel_size; + } + + float vbegin = 0.0; + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_FILL: + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (total_h - line_spacing * pixel_size) / 2.0; + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = (total_h - line_spacing * pixel_size); + } break; + } + + Vector2 offset = Vector2(0, vbegin + lbl_offset.y * pixel_size); + for (int i = 0; i < lines_rid.size(); i++) { + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + float line_width = TS->shaped_text_get_width(lines_rid[i]) * 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; + } + offset.x += lbl_offset.x * pixel_size; + offset.y -= (TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP)) * pixel_size; + + if (outline_modulate.a != 0.0 && outline_size > 0) { + // Outline surfaces. + Vector2 ol_offset = offset; + for (int j = 0; j < gl_size; j++) { + _generate_glyph_surfaces(glyphs[j], ol_offset, outline_modulate, outline_render_priority, outline_size); + } + } + + // Main text surfaces. + for (int j = 0; j < gl_size; j++) { + _generate_glyph_surfaces(glyphs[j], offset, modulate, render_priority); + } + offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM)) * pixel_size; + } + + for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) { + Array mesh_array; + mesh_array.resize(RS::ARRAY_MAX); + 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.value.material; + + RS::get_singleton()->mesh_add_surface(mesh, sd); + } +} + +void Label3D::set_text(const String &p_string) { + text = p_string; + xl_text = tr(p_string); + dirty_text = true; + _queue_update(); +} + +String Label3D::get_text() const { + return text; +} + +void Label3D::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (horizontal_alignment != p_alignment) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + dirty_lines = true; // Reshape lines. + } + horizontal_alignment = p_alignment; + _queue_update(); + } +} + +HorizontalAlignment Label3D::get_horizontal_alignment() const { + return horizontal_alignment; +} + +void Label3D::set_vertical_alignment(VerticalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (vertical_alignment != p_alignment) { + vertical_alignment = p_alignment; + _queue_update(); + } +} + +VerticalAlignment Label3D::get_vertical_alignment() const { + return vertical_alignment; +} + +void Label3D::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; + _queue_update(); + } +} + +TextServer::Direction Label3D::get_text_direction() const { + return text_direction; +} + +void Label3D::clear_opentype_features() { + opentype_features.clear(); + dirty_font = true; + _queue_update(); +} + +void Label3D::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; + _queue_update(); + } +} + +int Label3D::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 Label3D::set_language(const String &p_language) { + if (language != p_language) { + language = p_language; + dirty_text = true; + _queue_update(); + } +} + +String Label3D::get_language() const { + return language; +} + +void Label3D::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { + if (st_parser != p_parser) { + st_parser = p_parser; + dirty_text = true; + _queue_update(); + } +} + +TextServer::StructuredTextParser Label3D::get_structured_text_bidi_override() const { + return st_parser; +} + +void Label3D::set_structured_text_bidi_override_options(Array p_args) { + if (st_args != p_args) { + st_args = p_args; + dirty_text = true; + _queue_update(); + } +} + +Array Label3D::get_structured_text_bidi_override_options() const { + return st_args; +} + +void Label3D::set_uppercase(bool p_uppercase) { + if (uppercase != p_uppercase) { + uppercase = p_uppercase; + dirty_text = true; + _queue_update(); + } +} + +bool Label3D::is_uppercase() const { + return uppercase; +} + +void Label3D::set_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + if (render_priority != p_priority) { + render_priority = p_priority; + _queue_update(); + } +} + +int Label3D::get_render_priority() const { + return render_priority; +} + +void Label3D::set_outline_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + if (outline_render_priority != p_priority) { + outline_render_priority = p_priority; + _queue_update(); + } +} + +int Label3D::get_outline_render_priority() const { + return outline_render_priority; +} + +void Label3D::_font_changed() { + dirty_font = true; + _queue_update(); +} + +void Label3D::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; + if (font_override.is_valid()) { + font_override->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_font_changed")); + } + _queue_update(); + } +} + +Ref<Font> Label3D::get_font() const { + return font_override; +} + +Ref<Font> Label3D::_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 Label3D::set_font_size(int p_size) { + if (font_size != p_size) { + font_size = p_size; + dirty_font = true; + _queue_update(); + } +} + +int Label3D::get_font_size() const { + return font_size; +} + +void Label3D::set_outline_size(int p_size) { + if (outline_size != p_size) { + outline_size = p_size; + _queue_update(); + } +} + +int Label3D::get_outline_size() const { + return outline_size; +} + +void Label3D::set_modulate(const Color &p_color) { + if (modulate != p_color) { + modulate = p_color; + _queue_update(); + } +} + +Color Label3D::get_modulate() const { + return modulate; +} + +void Label3D::set_outline_modulate(const Color &p_color) { + if (outline_modulate != p_color) { + outline_modulate = p_color; + _queue_update(); + } +} + +Color Label3D::get_outline_modulate() const { + return outline_modulate; +} + +void Label3D::set_autowrap_mode(Label3D::AutowrapMode p_mode) { + if (autowrap_mode != p_mode) { + autowrap_mode = p_mode; + dirty_lines = true; + _queue_update(); + } +} + +Label3D::AutowrapMode Label3D::get_autowrap_mode() const { + return autowrap_mode; +} + +void Label3D::set_width(float p_width) { + if (width != p_width) { + width = p_width; + dirty_lines = true; + _queue_update(); + } +} + +float Label3D::get_width() const { + return width; +} + +void Label3D::set_pixel_size(real_t p_amount) { + if (pixel_size != p_amount) { + pixel_size = p_amount; + _queue_update(); + } +} + +real_t Label3D::get_pixel_size() const { + return pixel_size; +} + +void Label3D::set_offset(const Point2 &p_offset) { + if (lbl_offset != p_offset) { + lbl_offset = p_offset; + _queue_update(); + } +} + +Point2 Label3D::get_offset() const { + return lbl_offset; +} + +void Label3D::set_line_spacing(float p_line_spacing) { + if (line_spacing != p_line_spacing) { + line_spacing = p_line_spacing; + _queue_update(); + } +} + +float Label3D::get_line_spacing() const { + return line_spacing; +} + +void Label3D::set_draw_flag(DrawFlags p_flag, bool p_enable) { + ERR_FAIL_INDEX(p_flag, FLAG_MAX); + if (flags[p_flag] != p_enable) { + flags[p_flag] = p_enable; + _queue_update(); + } +} + +bool Label3D::get_draw_flag(DrawFlags p_flag) const { + ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); + return flags[p_flag]; +} + +void Label3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { + ERR_FAIL_INDEX(p_mode, 3); + if (billboard_mode != p_mode) { + billboard_mode = p_mode; + _queue_update(); + } +} + +StandardMaterial3D::BillboardMode Label3D::get_billboard_mode() const { + return billboard_mode; +} + +void Label3D::set_alpha_cut_mode(AlphaCutMode p_mode) { + ERR_FAIL_INDEX(p_mode, 3); + if (alpha_cut != p_mode) { + alpha_cut = p_mode; + _queue_update(); + } +} + +void Label3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { + if (texture_filter != p_filter) { + texture_filter = p_filter; + _queue_update(); + } +} + +StandardMaterial3D::TextureFilter Label3D::get_texture_filter() const { + return texture_filter; +} + +Label3D::AlphaCutMode Label3D::get_alpha_cut_mode() const { + return alpha_cut; +} + +void Label3D::set_alpha_scissor_threshold(float p_threshold) { + if (alpha_scissor_threshold != p_threshold) { + alpha_scissor_threshold = p_threshold; + _queue_update(); + } +} + +float Label3D::get_alpha_scissor_threshold() const { + return alpha_scissor_threshold; +} + +Label3D::Label3D() { + for (int i = 0; i < FLAG_MAX; i++) { + flags[i] = (i == FLAG_DOUBLE_SIDED); + } + + text_rid = TS->create_shaped_text(); + + mesh = RenderingServer::get_singleton()->mesh_create(); + + set_cast_shadows_setting(SHADOW_CASTING_SETTING_OFF); + set_base(mesh); +} + +Label3D::~Label3D() { + for (int i = 0; i < lines_rid.size(); i++) { + TS->free_rid(lines_rid[i]); + } + lines_rid.clear(); + + TS->free_rid(text_rid); + + RenderingServer::get_singleton()->free(mesh); + 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 new file mode 100644 index 0000000000..f57797a247 --- /dev/null +++ b/scene/3d/label_3d.h @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* label_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef LABEL_3D_H +#define LABEL_3D_H + +#include "scene/3d/visual_instance_3d.h" +#include "scene/resources/font.h" + +#include "servers/text_server.h" + +class Label3D : public GeometryInstance3D { + GDCLASS(Label3D, GeometryInstance3D); + +public: + enum DrawFlags { + FLAG_SHADED, + FLAG_DOUBLE_SIDED, + FLAG_DISABLE_DEPTH_TEST, + FLAG_FIXED_SIZE, + FLAG_MAX + }; + + enum AlphaCutMode { + ALPHA_CUT_DISABLED, + ALPHA_CUT_DISCARD, + ALPHA_CUT_OPAQUE_PREPASS + }; + + enum AutowrapMode { + AUTOWRAP_OFF, + AUTOWRAP_ARBITRARY, + AUTOWRAP_WORD, + AUTOWRAP_WORD_SMART + }; + +private: + real_t pixel_size = 0.01; + bool flags[FLAG_MAX] = {}; + AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; + float alpha_scissor_threshold = 0.5; + + AABB aabb; + + mutable Ref<TriangleMesh> triangle_mesh; + RID mesh; + struct SurfaceData { + PackedVector3Array mesh_vertices; + PackedVector3Array mesh_normals; + PackedFloat32Array mesh_tangents; + PackedColorArray mesh_colors; + PackedVector2Array mesh_uvs; + PackedInt32Array indices; + int offset = 0; + float z_shift = 0.0; + RID material; + }; + + HashMap<uint64_t, SurfaceData> surfaces; + + HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; + VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER; + String text; + String xl_text; + bool uppercase = false; + + AutowrapMode autowrap_mode = AUTOWRAP_OFF; + float width = 500.0; + + int font_size = 16; + Ref<Font> font_override; + Color modulate = Color(1, 1, 1, 1); + Point2 lbl_offset; + int outline_render_priority = -1; + int render_priority = 0; + + int outline_size = 0; + Color outline_modulate = Color(0, 0, 0, 1); + + float line_spacing = 0.f; + + Dictionary opentype_features; + String language; + TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; + Array st_args; + + RID text_rid; + Vector<RID> lines_rid; + + RID base_material; + StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; + StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; + + bool pending_update = false; + + bool dirty_lines = true; + bool dirty_font = true; + bool dirty_text = true; + + void _generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority = 0, int p_outline_size = 0); + +protected: + GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) + + void _notification(int p_what); + + static void _bind_methods(); + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + void _validate_property(PropertyInfo &property) const override; + + void _im_update(); + void _font_changed(); + void _queue_update(); + + void _shape(); + +public: + void set_horizontal_alignment(HorizontalAlignment p_alignment); + HorizontalAlignment get_horizontal_alignment() const; + + void set_vertical_alignment(VerticalAlignment p_alignment); + VerticalAlignment get_vertical_alignment() const; + + void set_render_priority(int p_priority); + int get_render_priority() const; + + void set_outline_render_priority(int p_priority); + int get_outline_render_priority() const; + + void set_text(const String &p_string); + String get_text() 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_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_outline_size(int p_size); + int get_outline_size() const; + + void set_line_spacing(float p_size); + float get_line_spacing() const; + + void set_modulate(const Color &p_color); + Color get_modulate() const; + + void set_outline_modulate(const Color &p_color); + Color get_outline_modulate() const; + + void set_autowrap_mode(AutowrapMode p_mode); + AutowrapMode get_autowrap_mode() const; + + void set_width(float p_width); + float get_width() const; + + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; + + void set_offset(const Point2 &p_offset); + Point2 get_offset() const; + + void set_draw_flag(DrawFlags p_flag, bool p_enable); + bool get_draw_flag(DrawFlags p_flag) const; + + void set_alpha_cut_mode(AlphaCutMode p_mode); + AlphaCutMode get_alpha_cut_mode() const; + + void set_alpha_scissor_threshold(float p_threshold); + float get_alpha_scissor_threshold() const; + + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); + StandardMaterial3D::BillboardMode get_billboard_mode() const; + + void set_texture_filter(StandardMaterial3D::TextureFilter p_filter); + StandardMaterial3D::TextureFilter get_texture_filter() const; + + virtual AABB get_aabb() const override; + Ref<TriangleMesh> generate_triangle_mesh() const; + + Label3D(); + ~Label3D(); +}; + +VARIANT_ENUM_CAST(Label3D::AutowrapMode); +VARIANT_ENUM_CAST(Label3D::DrawFlags); +VARIANT_ENUM_CAST(Label3D::AlphaCutMode); + +#endif // LABEL_3D_H diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 191a04b6a0..5c63bdcf1d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -318,12 +318,11 @@ void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &m int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { int over = 0; int under = 0; - int coplanar = 0; const BSPSimplex &s = p_simplices[p_simplex]; for (int i = 0; i < 4; i++) { const Vector3 v = p_points[s.vertices[i]]; - if (p_plane.has_point(v)) { //coplanar - coplanar++; + if (p_plane.has_point(v)) { + // Coplanar. } else if (p_plane.is_point_over(v)) { over++; } else { @@ -884,16 +883,16 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Light3D *light = lights_found[i].light; Transform3D xf = lights_found[i].xform; - Color linear_color = light->get_color().to_linear(); + Color linear_color = light->get_color().srgb_to_linear(); if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE)); + lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE)); } else if (Object::cast_to<OmniLight3D>(light)) { OmniLight3D *l = Object::cast_to<OmniLight3D>(light); lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); } else if (Object::cast_to<SpotLight3D>(light)) { SpotLight3D *l = Object::cast_to<SpotLight3D>(light); - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); + lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -1101,7 +1100,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa //#define DEBUG_SIMPLICES_AS_OBJ_FILE #ifdef DEBUG_SIMPLICES_AS_OBJ_FILE { - FileAccessRef f = FileAccess::open("res://bsp.obj", FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open("res://bsp.obj", FileAccess::WRITE); for (uint32_t i = 0; i < bsp_simplices.size(); i++) { f->store_line("o Simplex" + itos(i)); for (int j = 0; j < 4; j++) { @@ -1118,7 +1117,6 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa f->store_line(vformat("f %d %d %d", 4 * i + face_order[j][0], 4 * i + face_order[j][1], 4 * i + face_order[j][2])); } } - f->close(); } #endif @@ -1150,7 +1148,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } //#define DEBUG_BSP_TREE #ifdef DEBUG_BSP_TREE - FileAccessRef f = FileAccess::open("res://bsp.txt", FileAccess::WRITE); + Ref<FileAccess> f = FileAccess::open("res://bsp.txt", FileAccess::WRITE); for (uint32_t i = 0; i < bsp_nodes.size(); i++) { f->store_line(itos(i) + " - plane: " + bsp_nodes[i].plane + " over: " + itos(bsp_nodes[i].over) + " under: " + itos(bsp_nodes[i].under)); } 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..189ab7fc3b 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; } 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.h b/scene/3d/navigation_agent_3d.h index f4afebb36e..283b99a24f 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -45,19 +45,19 @@ class NavigationAgent3D : public Node { uint32_t navigable_layers = 1; real_t target_desired_distance = 1.0; - real_t radius; + real_t radius = 0.0; real_t navigation_height_offset = 0.0; - bool ignore_y; - real_t neighbor_dist; - int max_neighbors; - real_t time_horizon; - real_t max_speed; + bool ignore_y = false; + real_t neighbor_dist = 0.0; + int max_neighbors = 0; + real_t time_horizon = 0.0; + real_t max_speed = 0.0; real_t path_max_distance = 3.0; Vector3 target_location; Vector<Vector3> navigation_path; - int nav_path_index; + int nav_path_index = 0; bool velocity_submitted = false; Vector3 prev_safe_velocity; /// The submitted target velocity @@ -65,7 +65,7 @@ class NavigationAgent3D : public Node { bool target_reached = false; bool navigation_finished = true; // No initialized on purpose - uint32_t update_frame_id; + uint32_t update_frame_id = 0; protected: static void _bind_methods(); diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index 78dbecc0c5..fa6a633dee 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); @@ -79,7 +81,7 @@ void NavigationObstacle3D::_notification(int p_what) { } 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 +109,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 +136,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 +153,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_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 215e18869a..6404432631 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -73,6 +73,10 @@ uint32_t NavigationRegion3D::get_layers() const { return NavigationServer3D::get_singleton()->region_get_layers(region); } +RID NavigationRegion3D::get_region_rid() const { + return region; +} + ///////////////////////////// void NavigationRegion3D::_notification(int p_what) { @@ -161,13 +165,22 @@ void _bake_navigation_mesh(void *p_user_data) { } } -void NavigationRegion3D::bake_navigation_mesh() { +void NavigationRegion3D::bake_navigation_mesh(bool p_on_thread) { ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh."); BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - bake_thread.start(_bake_navigation_mesh, args); + 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); + } } void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { @@ -198,7 +211,9 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion3D::set_layers); ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion3D::get_layers); - ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh); + ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion3D::get_region_rid); + + ClassDB::bind_method(D_METHOD("bake_navigation_mesh", "on_thread"), &NavigationRegion3D::bake_navigation_mesh, DEFVAL(true)); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 1a50bb5f64..140dfebf6a 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -57,12 +57,14 @@ public: void set_layers(uint32_t p_layers); uint32_t get_layers() const; + RID get_region_rid() const; + void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; - /// Bakes the navigation mesh in a dedicated thread; once done, automatically + /// Bakes the navigation mesh; once done, automatically /// sets the new navigation mesh and emits a signal - void bake_navigation_mesh(); + void bake_navigation_mesh(bool p_on_thread); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 62cc7c143b..bbc977647e 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -181,15 +181,6 @@ 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); - if (!data.gizmos_disabled) { - for (int i = 0; i < data.gizmos.size(); i++) { - data.gizmos.write[i]->create(); - if (is_visible_in_tree()) { - data.gizmos.write[i]->redraw(); - } - data.gizmos.write[i]->transform(); - } - } } #endif } break; @@ -427,6 +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); return; } if (data.gizmos_dirty) { diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 7a5cb26a29..17a68f38f5 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -146,7 +146,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { Vector3 sideways = up.cross(forward).normalized(); up = forward.cross(sideways).normalized(); - t.basis.set(sideways, up, forward); + t.basis.set_columns(sideways, up, forward); t.basis.scale_local(scale); t.origin = pos + sideways * h_offset + up * v_offset; @@ -310,7 +310,7 @@ void PathFollow3D::set_offset(real_t p_offset) { if (path->get_curve().is_valid()) { real_t path_length = path->get_curve()->get_baked_length(); - if (loop) { + if (loop && path_length) { offset = Math::fposmod(offset, path_length); if (!Math::is_zero_approx(p_offset) && Math::is_zero_approx(offset)) { offset = path_length; diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index dee76aef10..94cd5400db 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -96,7 +96,11 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_distance, bool p parameters.max_collisions = p_max_collisions; PhysicsServer3D::MotionResult result; - if (move_and_collide(parameters, result, p_test_only)) { + + 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) { // 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(); @@ -386,18 +390,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; @@ -408,17 +412,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; @@ -432,43 +436,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)); @@ -477,7 +481,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); @@ -543,7 +547,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; @@ -554,7 +558,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; @@ -564,7 +568,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 @@ -987,7 +991,7 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) { + if (ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05) { warnings.push_back(RTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } @@ -1561,8 +1565,8 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) { } } -void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (collision_state.floor || !was_on_floor || vel_dir_facing_up) { +void CharacterBody3D::_snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up) { + if (collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) { return; } @@ -1596,8 +1600,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } } -bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { +bool CharacterBody3D::_on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up) { + if (up_direction == Vector3() || collision_state.floor || !p_was_on_floor || p_vel_dir_facing_up) { return false; } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 6ace681021..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; @@ -474,11 +474,11 @@ private: Ref<KinematicCollision3D> _get_slide_collision(int p_bounce); Ref<KinematicCollision3D> _get_last_slide_collision(); const Vector3 &get_up_direction() const; - bool _on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up); + bool _on_floor_if_snapped(bool p_was_on_floor, bool p_vel_dir_facing_up); void set_up_direction(const Vector3 &p_up_direction); void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result, CollisionState &r_state, CollisionState p_apply_state = CollisionState(true, true, true)); void _set_platform_data(const PhysicsServer3D::MotionCollision &p_collision); - void _snap_on_floor(bool was_on_floor, bool vel_dir_facing_up); + void _snap_on_floor(bool p_was_on_floor, bool p_vel_dir_facing_up); protected: void _notification(int p_what); @@ -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 783edf7fc6..f4a7767c44 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -263,7 +263,7 @@ 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()) { + for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { const Skin *skin = E->get()->skin.operator->(); RID skeleton = E->get()->skeleton; uint32_t bind_count = skin->get_bind_count(); @@ -509,6 +509,7 @@ void Skeleton3D::add_bone(const String &p_name) { bones.push_back(b); process_order_dirty = true; version++; + rest_dirty = true; _make_dirty(); update_gizmos(); } @@ -567,6 +568,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { bones.write[p_bone].parent = p_parent; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -585,6 +587,7 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { bones.write[p_bone].parent = -1; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -607,6 +610,7 @@ void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) { bones.write[p_bone].child_bones = p_children; process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -616,6 +620,7 @@ void Skeleton3D::add_bone_child(int p_bone, int p_child) { bones.write[p_bone].child_bones.push_back(p_child); process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -631,6 +636,7 @@ void Skeleton3D::remove_bone_child(int p_bone, int p_child) { } process_order_dirty = true; + rest_dirty = true; _make_dirty(); } @@ -643,6 +649,7 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].rest = p_rest; + rest_dirty = true; _make_dirty(); } Transform3D Skeleton3D::get_bone_rest(int p_bone) const { @@ -651,6 +658,14 @@ Transform3D Skeleton3D::get_bone_rest(int p_bone) const { return bones[p_bone].rest; } +Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + if (rest_dirty) { + const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } + return bones[p_bone].global_rest; +} void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { const int bone_size = bones.size(); @@ -985,7 +1000,7 @@ 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()) { + for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { if (E->get()->skin == p_skin) { return Ref<SkinReference>(E->get()); } @@ -1058,6 +1073,9 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { b.pose_global_no_override = b.pose_global; } } + if (rest_dirty) { + b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest; + } if (b.local_pose_override_amount >= CMP_EPSILON) { Transform3D override_local_pose; @@ -1088,6 +1106,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx); } + rest_dirty = false; } // Helper functions @@ -1206,6 +1225,7 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); + ClassDB::bind_method(D_METHOD("get_bone_global_rest", "bone_idx"), &Skeleton3D::get_bone_global_rest); ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms); ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin); @@ -1283,7 +1303,7 @@ Skeleton3D::Skeleton3D() { Skeleton3D::~Skeleton3D() { // Some skins may remain bound. - for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + for (RBSet<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { E->get()->skeleton_node = nullptr; } } diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 80ff2a1f79..c72792bd47 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -44,13 +44,13 @@ class SkinReference : public RefCounted { GDCLASS(SkinReference, RefCounted) friend class Skeleton3D; - Skeleton3D *skeleton_node; + Skeleton3D *skeleton_node = nullptr; RID skeleton; Ref<Skin> skin; uint32_t bind_count = 0; uint64_t skeleton_version = 0; Vector<uint32_t> skin_bone_indices; - uint32_t *skin_bone_indices_ptrs; + uint32_t *skin_bone_indices_ptrs = nullptr; void _skin_changed(); protected: @@ -77,6 +77,7 @@ private: int parent; Transform3D rest; + Transform3D global_rest; _FORCE_INLINE_ void update_pose_cache() { if (pose_cache_dirty) { @@ -130,18 +131,19 @@ private: } }; - Set<SkinReference *> skin_bindings; + RBSet<SkinReference *> skin_bindings; void _skin_changed(); bool animate_physical_bones = true; Vector<Bone> bones; - bool process_order_dirty; + bool process_order_dirty = false; Vector<int> parentless_bones; void _make_dirty(); bool dirty = false; + bool rest_dirty = false; bool show_rest_only = false; @@ -198,6 +200,7 @@ public: void set_bone_rest(int p_bone, const Transform3D &p_rest); Transform3D get_bone_rest(int p_bone) const; + Transform3D get_bone_global_rest(int p_bone) const; Transform3D get_bone_global_pose(int p_bone) const; Transform3D get_bone_global_pose_no_override(int p_bone) const; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index f29b060069..5e1f9d047f 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec } } -void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { +void FabrikInverseKinematic::solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet) { if (p_solve_magnet && !r_chain.middle_chain_item) { return; } diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 3ced5c49d3..0f656187de 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -103,7 +103,7 @@ private: static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos); /// Special solvers that solve only chains with one end effector - static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_backwards(const Chain &r_chain, bool p_solve_magnet); static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos); public: diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp index 6724754214..7d786a41bf 100644 --- a/scene/3d/soft_dynamic_body_3d.cpp +++ b/scene/3d/soft_dynamic_body_3d.cpp @@ -382,7 +382,7 @@ TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const { } Transform3D t = get_transform(); - if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { + if ((ABS(t.basis.get_column(0).length() - 1.0) > 0.05 || ABS(t.basis.get_column(1).length() - 1.0) > 0.05 || ABS(t.basis.get_column(2).length() - 1.0) > 0.05)) { warnings.push_back(RTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } 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/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 8d813e8b2b..8cb5081047 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -134,6 +134,16 @@ Color SpriteBase3D::get_modulate() const { return modulate; } +void SpriteBase3D::set_render_priority(int p_priority) { + ERR_FAIL_COND(p_priority < RS::MATERIAL_RENDER_PRIORITY_MIN || p_priority > RS::MATERIAL_RENDER_PRIORITY_MAX); + render_priority = p_priority; + _queue_update(); +} + +int SpriteBase3D::get_render_priority() const { + return render_priority; +} + void SpriteBase3D::set_pixel_size(real_t p_amount) { pixel_size = p_amount; _queue_update(); @@ -268,6 +278,17 @@ StandardMaterial3D::BillboardMode SpriteBase3D::get_billboard_mode() const { return billboard_mode; } +void SpriteBase3D::set_texture_filter(StandardMaterial3D::TextureFilter p_filter) { + if (texture_filter != p_filter) { + texture_filter = p_filter; + _queue_update(); + } +} + +StandardMaterial3D::TextureFilter SpriteBase3D::get_texture_filter() const { + return texture_filter; +} + void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_centered", "centered"), &SpriteBase3D::set_centered); ClassDB::bind_method(D_METHOD("is_centered"), &SpriteBase3D::is_centered); @@ -284,6 +305,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &SpriteBase3D::set_modulate); ClassDB::bind_method(D_METHOD("get_modulate"), &SpriteBase3D::get_modulate); + ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &SpriteBase3D::set_render_priority); + ClassDB::bind_method(D_METHOD("get_render_priority"), &SpriteBase3D::get_render_priority); + ClassDB::bind_method(D_METHOD("set_pixel_size", "pixel_size"), &SpriteBase3D::set_pixel_size); ClassDB::bind_method(D_METHOD("get_pixel_size"), &SpriteBase3D::get_pixel_size); @@ -299,6 +323,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode); ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode); + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &SpriteBase3D::set_texture_filter); + ClassDB::bind_method(D_METHOD("get_texture_filter"), &SpriteBase3D::get_texture_filter); + ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); @@ -317,11 +344,17 @@ void SpriteBase3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_draw_flag", "get_draw_flag", FLAG_DISABLE_DEPTH_TEST); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "fixed_size"), "set_draw_flag", "get_draw_flag", FLAG_FIXED_SIZE); ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); BIND_ENUM_CONSTANT(FLAG_TRANSPARENT); BIND_ENUM_CONSTANT(FLAG_SHADED); BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); + BIND_ENUM_CONSTANT(FLAG_DISABLE_DEPTH_TEST); + BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(ALPHA_CUT_DISABLED); @@ -544,7 +577,7 @@ void Sprite3D::_draw() { value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; if (t.d > 0) { - value |= 3 << 30; + value |= 3UL << 30; } v_tangent = value; } @@ -586,7 +619,7 @@ void Sprite3D::_draw() { set_aabb(aabb); RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); last_shader = shader_rid; @@ -595,6 +628,10 @@ void Sprite3D::_draw() { RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); last_texture = texture->get_rid(); } + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); + } } void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { @@ -907,7 +944,7 @@ void AnimatedSprite3D::_draw() { value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; if (t.d > 0) { - value |= 3 << 30; + value |= 3UL << 30; } v_tangent = value; } @@ -948,7 +985,7 @@ void AnimatedSprite3D::_draw() { set_aabb(aabb); RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); last_shader = shader_rid; @@ -957,6 +994,10 @@ void AnimatedSprite3D::_draw() { RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); last_texture = texture->get_rid(); } + if (get_alpha_cut_mode() == ALPHA_CUT_DISABLED) { + RS::get_singleton()->material_set_render_priority(get_material(), get_render_priority()); + RS::get_singleton()->mesh_surface_set_material(mesh, 0, get_material()); + } } void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { @@ -1206,6 +1247,17 @@ TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { return warnings; } +void AnimatedSprite3D::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + if (p_idx == 0 && p_function == "play" && frames.is_valid()) { + List<StringName> al; + frames->get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sprite_frames", "sprite_frames"), &AnimatedSprite3D::set_sprite_frames); ClassDB::bind_method(D_METHOD("get_sprite_frames"), &AnimatedSprite3D::get_sprite_frames); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 92dbef0855..6ac85a7bbc 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -44,6 +44,8 @@ public: FLAG_TRANSPARENT, FLAG_SHADED, FLAG_DOUBLE_SIDED, + FLAG_DISABLE_DEPTH_TEST, + FLAG_FIXED_SIZE, FLAG_MAX }; @@ -69,6 +71,7 @@ private: bool vflip = false; Color modulate = Color(1, 1, 1, 1); + int render_priority = 0; Vector3::Axis axis = Vector3::AXIS_Z; real_t pixel_size = 0.01; @@ -80,6 +83,7 @@ private: bool flags[FLAG_MAX] = {}; AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; + StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; bool pending_update = false; void _im_update(); @@ -97,10 +101,10 @@ protected: uint32_t mesh_surface_offsets[RS::ARRAY_MAX]; PackedByteArray vertex_buffer; PackedByteArray attribute_buffer; - uint32_t vertex_stride; - uint32_t attrib_stride; - uint32_t skin_stride; - uint32_t mesh_surface_format; + uint32_t vertex_stride = 0; + uint32_t attrib_stride = 0; + uint32_t skin_stride = 0; + uint32_t mesh_surface_format = 0; void _queue_update(); @@ -117,6 +121,9 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; + void set_render_priority(int p_priority); + int get_render_priority() const; + void set_modulate(const Color &p_color); Color get_modulate() const; @@ -131,9 +138,13 @@ public: void set_alpha_cut_mode(AlphaCutMode p_mode); AlphaCutMode get_alpha_cut_mode() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; + void set_texture_filter(StandardMaterial3D::TextureFilter p_filter); + StandardMaterial3D::TextureFilter get_texture_filter() const; + virtual Rect2 get_item_rect() const = 0; virtual AABB get_aabb() const override; @@ -237,6 +248,8 @@ public: virtual Rect2 get_item_rect() const override; virtual TypedArray<String> get_configuration_warnings() const override; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimatedSprite3D(); }; diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index f5a451ca04..19c90cdeb5 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -40,7 +40,7 @@ public: Vector3 m_0MinvJt; Vector3 m_1MinvJt; //Optimization: can be stored in the w/last component of one of the vectors - real_t m_Adiag; + real_t m_Adiag = 0.0; real_t getDiagonal() const { return m_Adiag; } @@ -90,8 +90,8 @@ void VehicleWheel3D::_notification(int p_what) { cb->wheels.push_back(this); m_chassisConnectionPointCS = get_transform().origin; - m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + m_wheelDirectionCS = -get_transform().basis.get_column(Vector3::AXIS_Y).normalized(); + m_wheelAxleCS = get_transform().basis.get_column(Vector3::AXIS_X).normalized(); } break; case NOTIFICATION_EXIT_TREE: { @@ -684,7 +684,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; - m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); + m_axle.write[i] = wheelBasis0.get_column(Vector3::AXIS_X); //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; 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.cpp b/scene/3d/voxelizer.cpp index bda3868fbb..42a2a68e2d 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -592,7 +592,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { bake_cells.write[p_idx].albedo[2] = 0; float alpha_average = 0; - int children_found = 0; for (int i = 0; i < 8; i++) { uint32_t child = bake_cells[p_idx].children[i]; @@ -603,8 +602,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { _fixup_plot(child, p_level + 1); alpha_average += bake_cells[child].alpha; - - children_found++; } bake_cells.write[p_idx].alpha = alpha_average / 8.0; @@ -777,8 +774,8 @@ Vector<int> Voxelizer::get_voxel_gi_level_cell_count() const { /* dt of 1d function using squared distance */ static void edt(float *f, int stride, int n) { float *d = (float *)alloca(sizeof(float) * n + sizeof(int) * n + sizeof(float) * (n + 1)); - int *v = (int *)&(d[n]); - float *z = (float *)&v[n]; + int *v = reinterpret_cast<int *>(&(d[n])); + float *z = reinterpret_cast<float *>(&v[n]); int k = 0; v[0] = 0; 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/3d/world_environment.cpp b/scene/3d/world_environment.cpp index f638644628..fe9d9ae4dd 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -71,7 +71,7 @@ void WorldEnvironment::_update_current_environment() { } else { get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); } - get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::_update_current_camera_effects() { @@ -82,7 +82,7 @@ void WorldEnvironment::_update_current_camera_effects() { get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); } - get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index 3085d84643..1dad6078b4 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -577,7 +577,7 @@ Plane XRAnchor3D::get_plane() const { Vector3 location = get_position(); Basis orientation = get_transform().basis; - Plane plane(orientation.get_axis(1).normalized(), location); + Plane plane(orientation.get_column(1).normalized(), location); return plane; } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index 2acacd7396..73bde633cb 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -354,7 +354,7 @@ class AnimationNodeBlendTree : public AnimationRootNode { Vector<StringName> connections; }; - Map<StringName, Node> nodes; + HashMap<StringName, Node> nodes; Vector2 graph_offset; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 5ea7f4b7d9..81df12791c 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -185,27 +185,29 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta return true; //nothing to do } - loops_current = 0; // reset loops, so fade does not happen immediately - 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; //build open list for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].from == current) { + if (p_state_machine->transitions[i].transition->is_disabled()) { + continue; + } + + if (p_state_machine->transitions[i].local_from == current) { open_list.push_back(i); - float cost = p_state_machine->states[p_state_machine->transitions[i].to].position.distance_to(current_pos); + float cost = p_state_machine->states[p_state_machine->transitions[i].local_to].position.distance_to(current_pos); cost *= p_state_machine->transitions[i].transition->get_priority(); AStarCost ap; ap.prev = current; ap.distance = cost; - cost_map[p_state_machine->transitions[i].to] = ap; + cost_map[p_state_machine->transitions[i].local_to] = ap; - if (p_state_machine->transitions[i].to == p_travel) { //prematurely found it! :D + if (p_state_machine->transitions[i].local_to == p_travel) { //prematurely found it! :D path.push_back(p_travel); return true; } @@ -224,8 +226,8 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta float least_cost = 1e20; for (List<int>::Element *E = open_list.front(); E; E = E->next()) { - float cost = cost_map[p_state_machine->transitions[E->get()].to].distance; - cost += p_state_machine->states[p_state_machine->transitions[E->get()].to].position.distance_to(target_pos); + float cost = cost_map[p_state_machine->transitions[E->get()].local_to].distance; + cost += p_state_machine->states[p_state_machine->transitions[E->get()].local_to].position.distance_to(target_pos); if (cost < least_cost) { least_cost_transition = E; @@ -233,34 +235,38 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta } } - StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].from; - StringName transition = p_state_machine->transitions[least_cost_transition->get()].to; + StringName transition_prev = p_state_machine->transitions[least_cost_transition->get()].local_from; + StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to; for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].from != transition || p_state_machine->transitions[i].to == transition_prev) { + if (p_state_machine->transitions[i].transition->is_disabled()) { + continue; + } + + if (p_state_machine->transitions[i].local_from != transition || p_state_machine->transitions[i].local_to == transition_prev) { continue; //not interested on those } - float distance = p_state_machine->states[p_state_machine->transitions[i].from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].to].position); + float distance = p_state_machine->states[p_state_machine->transitions[i].local_from].position.distance_to(p_state_machine->states[p_state_machine->transitions[i].local_to].position); distance *= p_state_machine->transitions[i].transition->get_priority(); - distance += cost_map[p_state_machine->transitions[i].from].distance; + distance += cost_map[p_state_machine->transitions[i].local_from].distance; - if (cost_map.has(p_state_machine->transitions[i].to)) { + if (cost_map.has(p_state_machine->transitions[i].local_to)) { //oh this was visited already, can we win the cost? - if (distance < cost_map[p_state_machine->transitions[i].to].distance) { - cost_map[p_state_machine->transitions[i].to].distance = distance; - cost_map[p_state_machine->transitions[i].to].prev = p_state_machine->transitions[i].from; + if (distance < cost_map[p_state_machine->transitions[i].local_to].distance) { + cost_map[p_state_machine->transitions[i].local_to].distance = distance; + cost_map[p_state_machine->transitions[i].local_to].prev = p_state_machine->transitions[i].local_from; } } else { //add to open list AStarCost ac; - ac.prev = p_state_machine->transitions[i].from; + ac.prev = p_state_machine->transitions[i].local_from; ac.distance = distance; - cost_map[p_state_machine->transitions[i].to] = ac; + cost_map[p_state_machine->transitions[i].local_to] = ac; open_list.push_back(i); - if (p_state_machine->transitions[i].to == p_travel) { + if (p_state_machine->transitions[i].local_to == p_travel) { found_route = true; break; } @@ -352,7 +358,6 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; - loops_current = 0; } if (!p_state_machine->states.has(current)) { @@ -388,12 +393,8 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } { //advance and loop check - float next_pos = len_current - rem; - - if (next_pos < pos_current) { - loops_current++; - } + end_loop = next_pos < pos_current; pos_current = next_pos; //looped } @@ -404,7 +405,11 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (path.size()) { for (int i = 0; i < p_state_machine->transitions.size(); i++) { - if (p_state_machine->transitions[i].from == current && p_state_machine->transitions[i].to == path[0]) { + if (p_state_machine->transitions[i].transition->is_disabled()) { + continue; + } + + if (p_state_machine->transitions[i].local_from == current && p_state_machine->transitions[i].local_to == path[0]) { next_xfade = p_state_machine->transitions[i].transition->get_xfade_time(); switch_mode = p_state_machine->transitions[i].transition->get_switch_mode(); next = path[0]; @@ -413,17 +418,39 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } else { float priority_best = 1e20; int auto_advance_to = -1; + for (int i = 0; i < p_state_machine->transitions.size(); i++) { - bool auto_advance = false; - if (p_state_machine->transitions[i].transition->has_auto_advance()) { - auto_advance = true; + if (p_state_machine->transitions[i].transition->is_disabled()) { + continue; } - StringName advance_condition_name = p_state_machine->transitions[i].transition->get_advance_condition_name(); - if (advance_condition_name != StringName() && bool(p_state_machine->get_parameter(advance_condition_name))) { - auto_advance = true; + + // handles end_node: when end_node is reached in a sub state machine, find and activate the current_transition + if (force_auto_advance) { + if (p_state_machine->transitions[i].from == current_transition.from && p_state_machine->transitions[i].to == current_transition.to) { + auto_advance_to = i; + force_auto_advance = false; + break; + } } - if (p_state_machine->transitions[i].from == current && auto_advance) { + // handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to + if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) { + if (p_state_machine->prev_state_machine.is_valid()) { + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + + if (prev_playback.is_valid()) { + StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", ""); + + if (p_state_machine->transitions[i].to == prev_local_to) { + auto_advance_to = i; + prev_playback->current_transition.next = StringName(); + break; + } + } + } + } + + if (p_state_machine->transitions[i].from == current && _check_advance_condition(p_state_machine, p_state_machine->transitions[i].transition)) { if (p_state_machine->transitions[i].transition->get_priority() <= priority_best) { priority_best = p_state_machine->transitions[i].transition->get_priority(); auto_advance_to = i; @@ -432,26 +459,69 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } if (auto_advance_to != -1) { - next = p_state_machine->transitions[auto_advance_to].to; + next = p_state_machine->transitions[auto_advance_to].local_to; + Transition tr; + tr.from = String(p_state_machine->state_machine_name) + "/" + String(p_state_machine->transitions[auto_advance_to].from); + tr.to = String(p_state_machine->transitions[auto_advance_to].to).replace_first("../", ""); + tr.next = p_state_machine->transitions[auto_advance_to].to; + current_transition = tr; next_xfade = p_state_machine->transitions[auto_advance_to].transition->get_xfade_time(); switch_mode = p_state_machine->transitions[auto_advance_to].transition->get_switch_mode(); } } + if (next == p_state_machine->end_node) { + Ref<AnimationNodeStateMachine> prev_state_machine = p_state_machine->prev_state_machine; + + if (prev_state_machine.is_valid()) { + Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback"); + + if (prev_playback.is_valid()) { + if (next_xfade) { + prev_playback->current_transition = current_transition; + prev_playback->force_auto_advance = true; + + return rem; + } + float priority_best = 1e20; + int auto_advance_to = -1; + + for (int i = 0; i < prev_state_machine->transitions.size(); i++) { + if (prev_state_machine->transitions[i].transition->is_disabled()) { + continue; + } + + if (current_transition.next == prev_state_machine->end_node && _check_advance_condition(prev_state_machine, prev_state_machine->transitions[i].transition)) { + if (prev_state_machine->transitions[i].transition->get_priority() <= priority_best) { + priority_best = prev_state_machine->transitions[i].transition->get_priority(); + auto_advance_to = i; + } + } + } + + if (auto_advance_to != -1) { + if (prev_state_machine->transitions[auto_advance_to].transition->get_xfade_time()) { + return rem; + } + } + } + } + } + //if next, see when to transition if (next != StringName()) { bool goto_next = false; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_AT_END) { - goto_next = next_xfade >= (len_current - pos_current) || loops_current > 0; - if (loops_current > 0) { + goto_next = next_xfade >= (len_current - pos_current) || end_loop; + if (end_loop) { next_xfade = 0; } } else { goto_next = fading_from == StringName(); } - if (goto_next) { //loops should be used because fade time may be too small or zero and animation may have looped + if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped if (next_xfade) { //time to fade, baby @@ -478,18 +548,38 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } rem = len_current; //so it does not show 0 on transition - loops_current = 0; } } - //compute time left for transitions by using the end node - if (p_state_machine->end_node != StringName() && p_state_machine->end_node != current) { - rem = p_state_machine->blend_node(p_state_machine->end_node, p_state_machine->states[p_state_machine->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); + // time left must always be 1 because the end node don't lenght to compute + if (p_state_machine->end_node != current) { + rem = 1; + } else { + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + + if (prev_playback.is_valid()) { + prev_playback->current_transition = current_transition; + prev_playback->force_auto_advance = true; + } } return rem; } +bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const { + if (transition->has_auto_advance()) { + return true; + } + + StringName advance_condition_name = transition->get_advance_condition_name(); + + if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) { + return true; + } + + return false; +} + void AnimationNodeStateMachinePlayback::_bind_methods() { ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); @@ -521,6 +611,23 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c for (const StringName &E : advance_conditions) { r_list->push_back(PropertyInfo(Variant::BOOL, E)); } + + // for (const KeyValue<StringName, State> &E : states) { + // if (E->node == ansm) { + // for (int i = 0; i < E->node->transitions.size(); i++) { + // StringName ac = E->node->transitions[i].transition->get_advance_condition_name(); + // if (ac != StringName() && advance_conditions.find(ac) == nullptr) { + // advance_conditions.push_back(ac); + // } + // } + + // advance_conditions.sort_custom<StringName::AlphCompare>(); + + // for (const StringName &E : advance_conditions) { + // r_list->push_back(PropertyInfo(Variant::BOOL, E)); + // } + // } + // } } Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { @@ -544,6 +651,13 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation states[p_name] = state; + Ref<AnimationNodeStateMachine> anodesm = p_node; + + if (anodesm.is_valid()) { + anodesm->state_machine_name = p_name; + anodesm->prev_state_machine = (Ref<AnimationNodeStateMachine>)this; + } + emit_changed(); emit_signal(SNAME("tree_changed")); @@ -570,6 +684,14 @@ void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<Anima p_node->connect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); } +bool AnimationNodeStateMachine::can_edit_node(const StringName &p_name) const { + if (states.has(p_name)) { + return !(states[p_name].node->is_class("AnimationNodeStartState") || states[p_name].node->is_class("AnimationNodeEndState")); + } + + return true; +} + Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); @@ -610,36 +732,24 @@ bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { void AnimationNodeStateMachine::remove_node(const StringName &p_name) { ERR_FAIL_COND(!states.has(p_name)); - { - Ref<AnimationNode> node = states[p_name].node; - - ERR_FAIL_COND(node.is_null()); - - node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); + if (!can_edit_node(p_name)) { + return; } - states.erase(p_name); - //path.erase(p_name); - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name || transitions[i].to == p_name) { - transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); - transitions.remove_at(i); + if (transitions[i].local_from == p_name || transitions[i].local_to == p_name) { + remove_transition_by_index(i); i--; } } - if (start_node == p_name) { - start_node = StringName(); - } - - if (end_node == p_name) { - end_node = StringName(); + { + Ref<AnimationNode> node = states[p_name].node; + ERR_FAIL_COND(node.is_null()); + node->disconnect("tree_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); } - /*if (playing && current == p_name) { - stop(); - }*/ + states.erase(p_name); emit_changed(); emit_signal(SNAME("tree_changed")); @@ -648,39 +758,73 @@ void AnimationNodeStateMachine::remove_node(const StringName &p_name) { void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { ERR_FAIL_COND(!states.has(p_name)); ERR_FAIL_COND(states.has(p_new_name)); + ERR_FAIL_COND(!can_edit_node(p_name)); states[p_new_name] = states[p_name]; states.erase(p_name); + Ref<AnimationNodeStateMachine> anodesm = states[p_new_name].node; + if (anodesm.is_valid()) { + anodesm->state_machine_name = p_new_name; + } + for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name) { - transitions.write[i].from = p_new_name; + if (transitions[i].local_from == p_name) { + _rename_transition(transitions[i].from, String(transitions[i].from).replace_first(p_name, p_new_name)); } - if (transitions[i].to == p_name) { - transitions.write[i].to = p_new_name; + if (transitions[i].local_to == p_name) { + _rename_transition(transitions[i].to, String(transitions[i].to).replace_first(p_name, p_new_name)); } } - if (start_node == p_name) { - start_node = p_new_name; - } + emit_signal("tree_changed"); +} - if (end_node == p_name) { - end_node = p_new_name; +void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, const StringName &p_new_name) { + if (updating_transitions) { + return; } - /*if (playing && current == p_name) { - current = p_new_name; - }*/ + updating_transitions = true; + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name) { + Vector<String> path = String(transitions[i].to).split("/"); + if (path.size() > 1) { + if (path[0] == "..") { + prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); + } else { + ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_to].node)->_rename_transition("../" + p_name, "../" + p_new_name); + } + } + + transitions.write[i].from = p_new_name; + } - //path.clear(); //clear path - emit_signal(SNAME("tree_changed")); + if (transitions[i].to == p_name) { + Vector<String> path = String(transitions[i].from).split("/"); + if (path.size() > 1) { + if (path[0] == "..") { + prev_state_machine->_rename_transition(String(state_machine_name) + "/" + p_name, String(state_machine_name) + "/" + p_new_name); + } else { + ((Ref<AnimationNodeStateMachine>)states[transitions[i].local_from].node)->_rename_transition("../" + p_name, "../" + p_new_name); + } + } + + transitions.write[i].to = p_new_name; + } + + updating_transitions = false; + } } void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { List<StringName> nodes; for (const KeyValue<StringName, State> &E : states) { + if (E.key == end_node && !prev_state_machine.is_valid()) { + continue; + } + nodes.push_back(E.key); } nodes.sort_custom<StringName::AlphCompare>(); @@ -690,9 +834,16 @@ void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { } } +Ref<AnimationNodeStateMachine> AnimationNodeStateMachine::get_prev_state_machine() const { + return prev_state_machine; +} + bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + StringName from = _get_shortest_path(p_from); + StringName to = _get_shortest_path(p_to); + for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { + if (transitions[i].from == from && transitions[i].to == to) { return true; } } @@ -700,32 +851,148 @@ bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const S } int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + StringName from = _get_shortest_path(p_from); + StringName to = _get_shortest_path(p_to); + for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { + if (transitions[i].from == from && transitions[i].to == to) { return i; } } return -1; } +bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Ref<AnimationNodeStateMachine>> p_parents) const { + if (p_parents.is_empty()) { + Ref<AnimationNodeStateMachine> prev = (Ref<AnimationNodeStateMachine>)this; + while (prev.is_valid()) { + p_parents.push_back(prev); + prev = prev->prev_state_machine; + } + } + + if (states.has(p_name)) { + Ref<AnimationNodeStateMachine> anodesm = states[p_name].node; + + if (anodesm.is_valid() && p_parents.find(anodesm) != -1) { + return false; + } + + return true; + } + + String name = p_name; + Vector<String> path = name.split("/"); + + if (path.size() < 2) { + return false; + } + + if (path[0] == "..") { + if (prev_state_machine.is_valid()) { + return prev_state_machine->_can_connect(name.replace_first("../", ""), p_parents); + } + } else if (states.has(path[0])) { + Ref<AnimationNodeStateMachine> anodesm = states[path[0]].node; + if (anodesm.is_valid()) { + return anodesm->_can_connect(name.replace_first(path[0] + "/", ""), p_parents); + } + } + + return false; +} + +StringName AnimationNodeStateMachine::_get_shortest_path(const StringName &p_path) const { + // If p_path is something like StateMachine/../StateMachine2/State1, + // the result will be StateMachine2/State1. This avoid duplicate + // transitions when using add_transition. eg, this two calls is the same: + // + // add_transition("State1", "StateMachine/../State2", tr) + // add_transition("State1", "State2", tr) + // + // but the second call must be invalid because the transition already exists + + Vector<String> path = String(p_path).split("/"); + Vector<String> new_path; + + for (int i = 0; i < path.size(); i++) { + if (i > 0 && path[i] == ".." && new_path[i - 1] != "..") { + new_path.remove_at(i - 1); + } else { + new_path.push_back(path[i]); + } + } + + String result; + for (int i = 0; i < new_path.size(); i++) { + result += new_path[i] + "/"; + } + result.remove_at(result.length() - 1); + + return result; +} + void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { - ERR_FAIL_COND(p_from == p_to); - ERR_FAIL_COND(!states.has(p_from)); - ERR_FAIL_COND(!states.has(p_to)); + if (updating_transitions) { + return; + } + + StringName from = _get_shortest_path(p_from); + StringName to = _get_shortest_path(p_to); + Vector<String> path_from = String(from).split("/"); + Vector<String> path_to = String(to).split("/"); + + ERR_FAIL_COND(from == end_node || to == start_node); + ERR_FAIL_COND(from == to); + ERR_FAIL_COND(!_can_connect(from)); + ERR_FAIL_COND(!_can_connect(to)); ERR_FAIL_COND(p_transition.is_null()); for (int i = 0; i < transitions.size(); i++) { - ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); + ERR_FAIL_COND(transitions[i].from == from && transitions[i].to == to); + } + + if (path_from.size() > 1 || path_to.size() > 1) { + ERR_FAIL_COND(path_from[0] == path_to[0]); } + updating_transitions = true; + + StringName local_from = String(from).get_slicec('/', 0); + StringName local_to = String(to).get_slicec('/', 0); + local_from = local_from == ".." ? "Start" : local_from; + local_to = local_to == ".." ? "End" : local_to; + Transition tr; - tr.from = p_from; - tr.to = p_to; + tr.from = from; + tr.to = to; + tr.local_from = local_from; + tr.local_to = local_to; tr.transition = p_transition; tr.transition->connect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed), varray(), CONNECT_REFERENCE_COUNTED); transitions.push_back(tr); + + // do recursive + if (path_from.size() > 1) { + StringName local_path = String(from).replace_first(path_from[0] + "/", ""); + if (path_from[0] == "..") { + prev_state_machine->add_transition(local_path, String(state_machine_name) + "/" + to, p_transition); + } else { + ((Ref<AnimationNodeStateMachine>)states[path_from[0]].node)->add_transition(local_path, "../" + to, p_transition); + } + } + if (path_to.size() > 1) { + StringName local_path = String(to).replace_first(path_to[0] + "/", ""); + if (path_to[0] == "..") { + prev_state_machine->add_transition(String(state_machine_name) + "/" + from, local_path, p_transition); + } else { + ((Ref<AnimationNodeStateMachine>)states[path_to[0]].node)->add_transition("../" + from, local_path, p_transition); + } + } + + updating_transitions = false; } Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { @@ -748,44 +1015,52 @@ int AnimationNodeStateMachine::get_transition_count() const { } void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { + StringName from = _get_shortest_path(p_from); + StringName to = _get_shortest_path(p_to); + for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { - transitions.write[i].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); - transitions.remove_at(i); + if (transitions[i].from == from && transitions[i].to == to) { + remove_transition_by_index(i); return; } } - - /*if (playing) { - path.clear(); - }*/ } -void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { +void AnimationNodeStateMachine::remove_transition_by_index(const int p_transition) { ERR_FAIL_INDEX(p_transition, transitions.size()); + Transition tr = transitions[p_transition]; transitions.write[p_transition].transition->disconnect("advance_condition_changed", callable_mp(this, &AnimationNodeStateMachine::_tree_changed)); transitions.remove_at(p_transition); - /*if (playing) { - path.clear(); - }*/ -} -void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - start_node = p_node; -} + Vector<String> path_from = String(tr.from).split("/"); + Vector<String> path_to = String(tr.to).split("/"); -String AnimationNodeStateMachine::get_start_node() const { - return start_node; -} + List<Vector<String>> paths; + paths.push_back(path_from); + paths.push_back(path_to); -void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - end_node = p_node; + for (List<Vector<String>>::Element *E = paths.front(); E; E = E->next()) { + if (E->get()[0].size() > 1) { + if (E->get()[0] == "..") { + prev_state_machine->_remove_transition(tr.transition); + } else if (states.has(E->get()[0])) { + Ref<AnimationNodeStateMachine> anodesm = states[E->get()[0]].node; + + if (anodesm.is_valid()) { + anodesm->_remove_transition(tr.transition); + } + } + } + } } -String AnimationNodeStateMachine::get_end_node() const { - return end_node; +void AnimationNodeStateMachine::_remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition) { + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].transition == p_transition) { + remove_transition_by_index(i); + return; + } + } } void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { @@ -807,6 +1082,18 @@ String AnimationNodeStateMachine::get_caption() const { return "StateMachine"; } +bool AnimationNodeStateMachine::has_local_transition(const StringName &p_from, const StringName &p_to) const { + StringName from = _get_shortest_path(p_from); + StringName to = _get_shortest_path(p_to); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].local_from == from && transitions[i].local_to == to) { + return true; + } + } + return false; +} + Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) { return get_node(p_name); } @@ -839,12 +1126,6 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_ add_transition(trans[i], trans[i + 1], trans[i + 2]); } return true; - } else if (name == "start_node") { - set_start_node(p_value); - return true; - } else if (name == "end_node") { - set_end_node(p_value); - return true; } else if (name == "graph_offset") { set_graph_offset(p_value); return true; @@ -860,7 +1141,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c String what = name.get_slicec('/', 2); if (what == "node") { - if (states.has(node_name)) { + if (states.has(node_name) && can_edit_node(node_name)) { r_ret = states[node_name].node; return true; } @@ -874,22 +1155,21 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c } } else if (name == "transitions") { Array trans; - trans.resize(transitions.size() * 3); - for (int i = 0; i < transitions.size(); i++) { - trans[i * 3 + 0] = transitions[i].from; - trans[i * 3 + 1] = transitions[i].to; - trans[i * 3 + 2] = transitions[i].transition; + String from = transitions[i].from; + String to = transitions[i].to; + + if (from.get_slicec('/', 0) == ".." || to.get_slicec('/', 0) == "..") { + continue; + } + + trans.push_back(from); + trans.push_back(to); + trans.push_back(transitions[i].transition); } r_ret = trans; return true; - } else if (name == "start_node") { - r_ret = get_start_node(); - return true; - } else if (name == "end_node") { - r_ret = get_end_node(); - return true; } else if (name == "graph_offset") { r_ret = get_graph_offset(); return true; @@ -911,8 +1191,6 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c } p_list->push_back(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "start_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); - p_list->push_back(PropertyInfo(Variant::STRING_NAME, "end_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR)); } @@ -920,10 +1198,24 @@ void AnimationNodeStateMachine::reset_state() { states.clear(); transitions.clear(); playback = "playback"; - start_node = StringName(); - end_node = StringName(); + start_node = "Start"; + end_node = "End"; graph_offset = Vector2(); + Ref<AnimationNodeStartState> s; + s.instantiate(); + State start; + start.node = s; + start.position = Vector2(200, 100); + states[start_node] = start; + + Ref<AnimationNodeEndState> e; + e.instantiate(); + State end; + end.node = e; + end.position = Vector2(900, 100); + states[end_node] = end; + emit_changed(); emit_signal(SNAME("tree_changed")); } @@ -963,15 +1255,22 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_transition_by_index", "idx"), &AnimationNodeStateMachine::remove_transition_by_index); ClassDB::bind_method(D_METHOD("remove_transition", "from", "to"), &AnimationNodeStateMachine::remove_transition); - ClassDB::bind_method(D_METHOD("set_start_node", "name"), &AnimationNodeStateMachine::set_start_node); - ClassDB::bind_method(D_METHOD("get_start_node"), &AnimationNodeStateMachine::get_start_node); - - ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node); - ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node); - ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); } AnimationNodeStateMachine::AnimationNodeStateMachine() { + Ref<AnimationNodeStartState> s; + s.instantiate(); + State start; + start.node = s; + start.position = Vector2(200, 100); + states[start_node] = start; + + Ref<AnimationNodeEndState> e; + e.instantiate(); + State end; + end.node = e; + end.position = Vector2(900, 100); + states[end_node] = end; } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index 3bae0fcffa..07d0579533 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -93,13 +93,19 @@ class AnimationNodeStateMachinePlayback : public Resource { StringName prev; }; - float len_total = 0.0; + struct Transition { + StringName from; + StringName to; + StringName next; + }; float len_current = 0.0; float pos_current = 0.0; - int loops_current = 0; + bool end_loop = false; StringName current; + Transition current_transition; + bool force_auto_advance = false; StringName fading_from; float fading_time = 0.0; @@ -116,6 +122,8 @@ class AnimationNodeStateMachinePlayback : public Resource { double process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek); + bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; + protected: static void _bind_methods(); @@ -144,24 +152,30 @@ private: Vector2 position; }; - Map<StringName, State> states; + HashMap<StringName, State> states; struct Transition { StringName from; StringName to; + StringName local_from; + StringName local_to; Ref<AnimationNodeStateMachineTransition> transition; }; Vector<Transition> transitions; StringName playback = "playback"; - - StringName start_node; - StringName end_node; + StringName state_machine_name; + Ref<AnimationNodeStateMachine> prev_state_machine; + bool updating_transitions = false; Vector2 graph_offset; void _tree_changed(); + void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition); + void _rename_transition(const StringName &p_name, const StringName &p_new_name); + bool _can_connect(const StringName &p_name, const Vector<Ref<AnimationNodeStateMachine>> p_parents = Vector<Ref<AnimationNodeStateMachine>>()) const; + StringName _get_shortest_path(const StringName &p_path) const; protected: static void _bind_methods(); @@ -169,10 +183,14 @@ protected: 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; + bool _check_advance_condition(const Ref<AnimationNodeStateMachine> p_state_machine, const Ref<AnimationNodeStateMachineTransition> p_transition) const; virtual void reset_state() override; public: + StringName start_node = "Start"; + StringName end_node = "End"; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const override; virtual Variant get_parameter_default_value(const StringName &p_parameter) const override; @@ -191,20 +209,19 @@ public: virtual void get_child_nodes(List<ChildNode> *r_child_nodes) override; bool has_transition(const StringName &p_from, const StringName &p_to) const; + bool has_local_transition(const StringName &p_from, const StringName &p_to) const; int find_transition(const StringName &p_from, const StringName &p_to) const; void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition); Ref<AnimationNodeStateMachineTransition> get_transition(int p_transition) const; StringName get_transition_from(int p_transition) const; StringName get_transition_to(int p_transition) const; int get_transition_count() const; - void remove_transition_by_index(int p_transition); + void remove_transition_by_index(const int p_transition); void remove_transition(const StringName &p_from, const StringName &p_to); - void set_start_node(const StringName &p_node); - String get_start_node() const; + bool can_edit_node(const StringName &p_name) const; - void set_end_node(const StringName &p_node); - String get_end_node() const; + Ref<AnimationNodeStateMachine> get_prev_state_machine() const; void set_graph_offset(const Vector2 &p_offset); Vector2 get_graph_offset() const; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 1ab2e2419e..9ace6db505 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -83,8 +83,31 @@ bool AnimationPlayer::_set(const StringName &p_name, const Variant &p_value) { set_current_animation(p_value); } else if (name.begins_with("anims/")) { + // Backwards compatibility with 3.x, add them to "default" library. String which = name.get_slicec('/', 1); - add_animation(which, p_value); + + Ref<Animation> anim = p_value; + Ref<AnimationLibrary> al; + if (!has_animation_library(StringName())) { + al.instantiate(); + add_animation_library(StringName(), al); + } else { + al = get_animation_library(StringName()); + } + al->add_animation(which, anim); + + } else if (name.begins_with("libraries")) { + Dictionary d = p_value; + while (animation_libraries.size()) { + remove_animation_library(animation_libraries[0].name); + } + List<Variant> keys; + d.get_key_list(&keys); + for (const Variant &K : keys) { + StringName lib_name = K; + Ref<AnimationLibrary> lib = d[lib_name]; + add_animation_library(lib_name, lib); + } } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); @@ -117,9 +140,13 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_current_animation(); - } else if (name.begins_with("anims/")) { - String which = name.get_slicec('/', 1); - r_ret = get_animation(which); + } else if (name.begins_with("libraries")) { + Dictionary d; + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + d[animation_libraries[i].name] = animation_libraries[i].library; + } + + r_ret = d; } else if (name.begins_with("next/")) { String which = name.get_slicec('/', 1); @@ -136,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; @@ -173,8 +200,9 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const { void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> anim_names; + anim_names.push_back(PropertyInfo(Variant::DICTIONARY, "libraries")); + for (const KeyValue<StringName, AnimationData> &E : animation_set) { - anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); if (E.value.next != StringName()) { anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); } @@ -256,7 +284,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov for (int i = 0; i < a->get_track_count(); i++) { p_anim->node_cache.write[i] = nullptr; - RES resource; + Ref<Resource> resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); ERR_CONTINUE_MSG(!child, "On Animation: '" + p_anim->name + "', couldn't resolve track: '" + String(a->track_get_path(i)) + "'."); // couldn't find the child node @@ -560,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); @@ -710,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) { @@ -1155,71 +1183,106 @@ void AnimationPlayer::_animation_process(double p_delta) { } } -Error AnimationPlayer::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) { -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + "."); -#endif +void AnimationPlayer::_animation_set_cache_update() { + // Relatively fast function to update all animations. - ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); + animation_set_update_pass++; + bool clear_cache_needed = false; - if (animation_set.has(p_name)) { - _unref_anim(animation_set[p_name].animation); - animation_set[p_name].animation = p_animation; - clear_caches(); - } else { - AnimationData ad; - ad.animation = p_animation; - ad.name = p_name; - animation_set[p_name] = ad; - } + // Update changed and add otherwise + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { + StringName key = animation_libraries[i].name == StringName() ? K.key : StringName(String(animation_libraries[i].name) + "/" + String(K.key)); + if (!animation_set.has(key)) { + AnimationData ad; + ad.animation = K.value; + ad.animation_library = animation_libraries[i].name; + ad.name = key; + ad.last_update = animation_set_update_pass; + animation_set.insert(ad.name, ad); + } else { + AnimationData &ad = animation_set[key]; + if (ad.last_update != animation_set_update_pass) { + // Was not updated, update. If the animation is duplicated, the second one will be ignored. + if (ad.animation != K.value || ad.animation_library != animation_libraries[i].name) { + // Animation changed, update and clear caches. + clear_cache_needed = true; + ad.animation = K.value; + ad.animation_library = animation_libraries[i].name; + } - _ref_anim(p_animation); - notify_property_list_changed(); - return OK; -} + ad.last_update = animation_set_update_pass; + } + } + } + } -void AnimationPlayer::remove_animation(const StringName &p_name) { - ERR_FAIL_COND(!animation_set.has(p_name)); + // Check removed + List<StringName> to_erase; + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + if (E.value.last_update != animation_set_update_pass) { + // Was not updated, must be erased + to_erase.push_back(E.key); + clear_cache_needed = true; + } + } - stop(); - _unref_anim(animation_set[p_name].animation); - animation_set.erase(p_name); + while (to_erase.size()) { + animation_set.erase(to_erase.front()->get()); + to_erase.pop_front(); + } - clear_caches(); - notify_property_list_changed(); + if (clear_cache_needed) { + // If something was modified or removed, caches need to be cleared + clear_caches(); + } } -void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED); -} +void AnimationPlayer::_animation_added(const StringName &p_name, const StringName &p_library) { + _animation_set_cache_update(); -void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { - Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed)); + update_configuration_warnings(); } -void AnimationPlayer::rename_animation(const StringName &p_name, const StringName &p_new_name) { - ERR_FAIL_COND(!animation_set.has(p_name)); - ERR_FAIL_COND(String(p_new_name).contains("/") || String(p_new_name).contains(":")); - ERR_FAIL_COND(animation_set.has(p_new_name)); +void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) { + StringName name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); - stop(); - AnimationData ad = animation_set[p_name]; - ad.name = p_new_name; - animation_set.erase(p_name); - animation_set[p_new_name] = ad; + if (!animation_set.has(name)) { + return; // No need to update because not the one from the library being used. + } + _animation_set_cache_update(); + + // Erase blends if needed + List<BlendKey> to_erase; + for (const KeyValue<BlendKey, float> &E : blend_times) { + BlendKey bk = E.key; + if (bk.from == name || bk.to == name) { + to_erase.push_back(bk); + } + } + + while (to_erase.size()) { + blend_times.erase(to_erase.front()->get()); + to_erase.pop_front(); + } + + update_configuration_warnings(); +} +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; bool erase = false; - if (bk.from == p_name) { - new_bk.from = p_new_name; + if (bk.from == p_from_name) { + new_bk.from = p_to_name; erase = true; } - if (bk.to == p_name) { - new_bk.to = p_new_name; + if (bk.to == p_from_name) { + new_bk.to = p_to_name; erase = true; } @@ -1235,16 +1298,188 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam } 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_name) { - autoplay = p_new_name; + if (autoplay == p_from_name) { + autoplay = p_to_name; } +} + +void AnimationPlayer::_animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library) { + StringName from_name = p_library == StringName() ? p_name : StringName(String(p_library) + "/" + String(p_name)); + StringName to_name = p_library == StringName() ? p_to_name : StringName(String(p_library) + "/" + String(p_to_name)); + + if (!animation_set.has(from_name)) { + return; // No need to update because not the one from the library being used. + } + _animation_set_cache_update(); + + _rename_animation(from_name, to_name); + update_configuration_warnings(); +} + +Error AnimationPlayer::add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library) { + ERR_FAIL_COND_V(p_animation_library.is_null(), ERR_INVALID_PARAMETER); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + "."); +#endif + + int insert_pos = 0; + + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + ERR_FAIL_COND_V_MSG(animation_libraries[i].name == p_name, ERR_ALREADY_EXISTS, "Can't add animation library twice with name: " + String(p_name)); + ERR_FAIL_COND_V_MSG(animation_libraries[i].library == p_animation_library, ERR_ALREADY_EXISTS, "Can't add animation library twice (adding as '" + p_name.operator String() + "', exists as '" + animation_libraries[i].name.operator String() + "'."); + + if (animation_libraries[i].name.operator String() >= p_name.operator String()) { + break; + } + + insert_pos++; + } + + AnimationLibraryData ald; + ald.name = p_name; + ald.library = p_animation_library; + + animation_libraries.insert(insert_pos, ald); + + ald.library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name)); + ald.library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_name)); + ald.library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_name)); + + _animation_set_cache_update(); + + notify_property_list_changed(); + + update_configuration_warnings(); + return OK; +} + +void AnimationPlayer::remove_animation_library(const StringName &p_name) { + int at_pos = -1; + + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + if (animation_libraries[i].name == p_name) { + at_pos = i; + break; + } + } + + ERR_FAIL_COND(at_pos == -1); + + animation_libraries[at_pos].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); + animation_libraries[at_pos].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added)); + animation_libraries[at_pos].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + + stop(); + + for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[at_pos].library->animations) { + _unref_anim(K.value); + } + + animation_libraries.remove_at(at_pos); + _animation_set_cache_update(); - clear_caches(); notify_property_list_changed(); + update_configuration_warnings(); +} + +void AnimationPlayer::_ref_anim(const Ref<Animation> &p_anim) { + Ref<Animation>(p_anim)->connect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed), varray(), CONNECT_REFERENCE_COUNTED); +} + +void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) { + Ref<Animation>(p_anim)->disconnect(SceneStringNames::get_singleton()->tracks_changed, callable_mp(this, &AnimationPlayer::_animation_changed)); +} + +void AnimationPlayer::rename_animation_library(const StringName &p_name, const StringName &p_new_name) { + if (p_name == p_new_name) { + return; + } +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(String(p_new_name).contains("/") || String(p_new_name).contains(":") || String(p_new_name).contains(",") || String(p_new_name).contains("["), "Invalid animation library name: " + String(p_new_name) + "."); +#endif + + bool found = false; + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + ERR_FAIL_COND_MSG(animation_libraries[i].name == p_new_name, "Can't rename animation library to another existing name: " + String(p_new_name)); + if (animation_libraries[i].name == p_name) { + found = true; + animation_libraries[i].name = p_new_name; + // rename connections + animation_libraries[i].library->disconnect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added)); + animation_libraries[i].library->disconnect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added)); + animation_libraries[i].library->disconnect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed)); + + animation_libraries[i].library->connect(SNAME("animation_added"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_removed"), callable_mp(this, &AnimationPlayer::_animation_added), varray(p_new_name)); + animation_libraries[i].library->connect(SNAME("animation_renamed"), callable_mp(this, &AnimationPlayer::_animation_renamed), varray(p_new_name)); + + for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { + StringName old_name = p_name == StringName() ? K.key : StringName(String(p_name) + "/" + String(K.key)); + StringName new_name = p_new_name == StringName() ? K.key : StringName(String(p_new_name) + "/" + String(K.key)); + _rename_animation(old_name, new_name); + } + } + } + + ERR_FAIL_COND(!found); + + stop(); + + animation_libraries.sort(); // Must keep alphabetical order. + + _animation_set_cache_update(); // Update cache. + + notify_property_list_changed(); +} + +bool AnimationPlayer::has_animation_library(const StringName &p_name) const { + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + if (animation_libraries[i].name == p_name) { + return true; + } + } + + return false; +} + +Ref<AnimationLibrary> AnimationPlayer::get_animation_library(const StringName &p_name) const { + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + if (animation_libraries[i].name == p_name) { + return animation_libraries[i].library; + } + } + ERR_FAIL_V(Ref<AnimationLibrary>()); +} + +TypedArray<StringName> AnimationPlayer::_get_animation_library_list() const { + TypedArray<StringName> ret; + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + ret.push_back(animation_libraries[i].name); + } + return ret; +} + +void AnimationPlayer::get_animation_library_list(List<StringName> *p_libraries) const { + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + p_libraries->push_back(animation_libraries[i].name); + } +} + +TypedArray<String> AnimationPlayer::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + for (uint32_t i = 0; i < animation_libraries.size(); i++) { + for (const KeyValue<StringName, Ref<Animation>> &K : animation_libraries[i].library->animations) { + if (animation_set.has(K.key) && animation_set[K.key].animation_library != animation_libraries[i].name) { + warnings.push_back(vformat(RTR("Animation '%s' in library '%s' is unused because another animation with the same name exists in library '%s'."), K.key, animation_libraries[i].name, animation_set[K.key].animation_library)); + } + } + } + return warnings; } bool AnimationPlayer::has_animation(const StringName &p_name) const { @@ -1531,7 +1766,7 @@ void AnimationPlayer::_animation_changed() { } void AnimationPlayer::_stop_playing_caches() { - for (Set<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) { + for (RBSet<TrackNodeCache *>::Element *E = playing_caches.front(); E; E = E->next()) { if (E->get()->node && E->get()->audio_playing) { E->get()->node->call(SNAME("stop")); } @@ -1585,7 +1820,16 @@ StringName AnimationPlayer::find_animation(const Ref<Animation> &p_animation) co } } - return ""; + return StringName(); +} + +StringName AnimationPlayer::find_animation_library(const Ref<Animation> &p_animation) const { + for (const KeyValue<StringName, AnimationData> &E : animation_set) { + if (E.value.animation == p_animation) { + return E.value.animation_library; + } + } + return StringName(); } void AnimationPlayer::set_autoplay(const String &p_name) { @@ -1764,7 +2008,10 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) { AnimationPlayer *aux_player = memnew(AnimationPlayer); EditorNode::get_singleton()->add_child(aux_player); - aux_player->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim); + Ref<AnimationLibrary> al; + al.instantiate(); + al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim); + aux_player->add_animation_library("default", al); aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET); // Forcing the use of the original root because the scene where original player belongs may be not the active one Node *root = get_node(get_root()); @@ -1792,9 +2039,13 @@ bool AnimationPlayer::can_apply_reset() const { #endif // TOOLS_ENABLED void AnimationPlayer::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationPlayer::add_animation); - ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationPlayer::remove_animation); - ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationPlayer::rename_animation); + ClassDB::bind_method(D_METHOD("add_animation_library", "name", "library"), &AnimationPlayer::add_animation_library); + ClassDB::bind_method(D_METHOD("remove_animation_library", "name"), &AnimationPlayer::remove_animation_library); + ClassDB::bind_method(D_METHOD("rename_animation_library", "name", "newname"), &AnimationPlayer::rename_animation_library); + ClassDB::bind_method(D_METHOD("has_animation_library", "name"), &AnimationPlayer::has_animation_library); + ClassDB::bind_method(D_METHOD("get_animation_library", "name"), &AnimationPlayer::get_animation_library); + ClassDB::bind_method(D_METHOD("get_animation_library_list"), &AnimationPlayer::_get_animation_library_list); + ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationPlayer::has_animation); ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationPlayer::get_animation); ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationPlayer::_get_animation_list); @@ -1838,6 +2089,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root); ClassDB::bind_method(D_METHOD("find_animation", "animation"), &AnimationPlayer::find_animation); + ClassDB::bind_method(D_METHOD("find_animation_library", "animation"), &AnimationPlayer::find_animation_library); ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationPlayer::clear_caches); diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index a68f6b9d5b..3ef87dba28 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -36,6 +36,7 @@ #include "scene/3d/node_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/resources/animation.h" +#include "scene/resources/animation_library.h" #ifdef TOOLS_ENABLED class AnimatedValuesBackup : public RefCounted { @@ -93,7 +94,7 @@ private: struct TrackNodeCache { NodePath path; uint32_t id = 0; - RES resource; + Ref<Resource> resource; Node *node = nullptr; Node2D *node_2d = nullptr; #ifndef _3D_DISABLED @@ -131,7 +132,7 @@ private: Variant capture; }; - Map<StringName, PropertyAnim> property_anim; + HashMap<StringName, PropertyAnim> property_anim; struct BezierAnim { Vector<StringName> bezier_property; @@ -141,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() {} @@ -152,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) { @@ -165,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; @@ -173,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; @@ -184,16 +195,39 @@ private: StringName next; Vector<TrackNodeCache *> node_cache; Ref<Animation> animation; + StringName animation_library; + uint64_t last_update = 0; + }; + + HashMap<StringName, AnimationData> animation_set; + + struct AnimationLibraryData { + StringName name; + Ref<AnimationLibrary> library; + bool operator<(const AnimationLibraryData &p_data) const { return name.operator String() < p_data.name.operator String(); } }; - Map<StringName, AnimationData> animation_set; + LocalVector<AnimationLibraryData> animation_libraries; + 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; @@ -261,6 +295,15 @@ private: bool playing = false; + uint64_t animation_set_update_pass = 1; + void _animation_set_cache_update(); + void _animation_added(const StringName &p_name, const StringName &p_library); + void _animation_removed(const StringName &p_name, const StringName &p_library); + void _animation_renamed(const StringName &p_name, const StringName &p_to_name, const StringName &p_library); + void _rename_animation(const StringName &p_from_name, const StringName &p_to_name); + + TypedArray<StringName> _get_animation_library_list() const; + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -272,13 +315,18 @@ protected: public: StringName find_animation(const Ref<Animation> &p_animation) const; + StringName find_animation_library(const Ref<Animation> &p_animation) const; + + Error add_animation_library(const StringName &p_name, const Ref<AnimationLibrary> &p_animation_library); + void remove_animation_library(const StringName &p_name); + void rename_animation_library(const StringName &p_name, const StringName &p_new_name); + Ref<AnimationLibrary> get_animation_library(const StringName &p_name) const; + void get_animation_library_list(List<StringName> *p_animations) const; + bool has_animation_library(const StringName &p_name) const; - Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation); - void remove_animation(const StringName &p_name); - void rename_animation(const StringName &p_name, const StringName &p_new_name); - bool has_animation(const StringName &p_name) const; Ref<Animation> get_animation(const StringName &p_name) const; void get_animation_list(List<StringName> *p_animations) const; + bool has_animation(const StringName &p_name) const; void set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time); float get_blend_time(const StringName &p_animation1, const StringName &p_animation2) const; @@ -340,6 +388,8 @@ public: bool can_apply_reset() const; #endif + TypedArray<String> get_configuration_warnings() const override; + AnimationPlayer(); ~AnimationPlayer(); }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 64c71697a5..92af15d5d3 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -198,12 +198,11 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri blendw[i] = 0.0; //all to zero by default } - const NodePath *K = nullptr; - while ((K = filter.next(K))) { - if (!state->track_map.has(*K)) { + for (const KeyValue<NodePath, bool> &E : filter) { + if (!state->track_map.has(E.key)) { continue; } - int idx = state->track_map[*K]; + int idx = state->track_map[E.key]; blendw[idx] = 1.0; //filtered goes to one } @@ -374,9 +373,8 @@ bool AnimationNode::has_filter() const { Array AnimationNode::_get_filters() const { Array paths; - const NodePath *K = nullptr; - while ((K = filter.next(K))) { - paths.push_back(String(*K)); //use strings, so sorting is possible + for (const KeyValue<NodePath, bool> &E : filter) { + paths.push_back(String(E.key)); //use strings, so sorting is possible } paths.sort(); //done so every time the scene is saved, it does not change @@ -488,7 +486,7 @@ void AnimationTree::set_active(bool p_active) { } if (!active && is_inside_tree()) { - for (Set<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) { + for (RBSet<TrackCache *>::Element *E = playing_caches.front(); E; E = E->next()) { if (ObjectDB::get_instance(E->get()->object_id)) { E->get()->object->call(SNAME("stop")); } @@ -540,6 +538,11 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { List<StringName> sname; player->get_animation_list(&sname); + Ref<Animation> reset_anim; + bool has_reset_anim = player->has_animation(SceneStringNames::get_singleton()->RESET); + if (has_reset_anim) { + reset_anim = player->get_animation(SceneStringNames::get_singleton()->RESET); + } for (const StringName &E : sname) { Ref<Animation> anim = player->get_animation(E); for (int i = 0; i < anim->get_track_count(); i++) { @@ -565,7 +568,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } if (!track) { - RES resource; + Ref<Resource> resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(path, resource, leftover_path); @@ -593,6 +596,12 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track = track_value; + if (has_reset_anim) { + int rt = reset_anim->find_track(path, track_type); + if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { + track_value->init_value = reset_anim->track_get_key_value(rt, 0); + } + } } break; case Animation::TYPE_POSITION_3D: case Animation::TYPE_ROTATION_3D: @@ -612,16 +621,17 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_xform->skeleton = nullptr; track_xform->bone_idx = -1; + bool has_rest = false; if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(node_3d)) { Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d); track_xform->skeleton = sk; int bone_idx = sk->find_bone(path.get_subname(0)); if (bone_idx != -1) { + has_rest = true; track_xform->bone_idx = bone_idx; Transform3D rest = sk->get_bone_rest(bone_idx); track_xform->init_loc = rest.origin; - track_xform->ref_rot = rest.basis.get_rotation_quaternion(); - track_xform->init_rot = track_xform->ref_rot.log(); + track_xform->init_rot = rest.basis.get_rotation_quaternion(); track_xform->init_scale = rest.basis.get_scale(); } } @@ -645,6 +655,25 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } } + // For non Skeleton3D bone animation. + if (has_reset_anim && !has_rest) { + int rt = reset_anim->find_track(path, track_type); + if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { + switch (track_type) { + case Animation::TYPE_POSITION_3D: { + track_xform->init_loc = reset_anim->track_get_key_value(rt, 0); + } break; + case Animation::TYPE_ROTATION_3D: { + track_xform->init_rot = reset_anim->track_get_key_value(rt, 0); + } break; + case Animation::TYPE_SCALE_3D: { + track_xform->init_scale = reset_anim->track_get_key_value(rt, 0); + } break; + default: { + } + } + } + } #endif // _3D_DISABLED } break; case Animation::TYPE_BLEND_SHAPE: { @@ -675,6 +704,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_bshape->object = mesh_3d; track_bshape->object_id = mesh_3d->get_instance_id(); track = track_bshape; + + if (has_reset_anim) { + int rt = reset_anim->find_track(path, track_type); + if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { + track_bshape->init_value = reset_anim->track_get_key_value(rt, 0); + } + } #endif } break; case Animation::TYPE_METHOD: { @@ -704,6 +740,13 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { track_bezier->object_id = track_bezier->object->get_instance_id(); track = track_bezier; + + if (has_reset_anim) { + int rt = reset_anim->find_track(path, track_type); + if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { + track_bezier->init_value = reset_anim->track_get_key_value(rt, 0); + } + } } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *track_audio = memnew(TrackCacheAudio); @@ -758,11 +801,10 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { List<NodePath> to_delete; - const NodePath *K = nullptr; - while ((K = track_cache.next(K))) { - TrackCache *tc = track_cache[*K]; + for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + TrackCache *tc = track_cache[K.key]; if (tc->setup_pass != setup_pass) { - to_delete.push_back(*K); + to_delete.push_back(K.key); } } @@ -775,10 +817,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { state.track_map.clear(); - K = nullptr; int idx = 0; - while ((K = track_cache.next(K))) { - state.track_map[*K] = idx; + for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + state.track_map[K.key] = idx; idx++; } @@ -790,9 +831,8 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { } void AnimationTree::_clear_caches() { - const NodePath *K = nullptr; - while ((K = track_cache.next(K))) { - memdelete(track_cache[*K]); + for (KeyValue<NodePath, TrackCache *> &K : track_cache) { + memdelete(K.value); } playing_caches.clear(); @@ -950,13 +990,13 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } if (track->root_motion) { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(0, 0, 0); + t->rot = Quaternion(0, 0, 0, 1); + t->scale = Vector3(0, 0, 0); + } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1026,6 +1066,12 @@ void AnimationTree::_process_graph(double p_delta) { prev_time = !backward ? 0 : (double)a->get_length(); } else { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = t->init_loc; + t->rot = t->init_rot; + t->scale = t->init_scale; + } Vector3 loc; Error err = a->position_track_interpolate(i, time, &loc); @@ -1040,13 +1086,13 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_ROTATION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } if (track->root_motion) { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(0, 0, 0); + t->rot = Quaternion(0, 0, 0, 1); + t->scale = Vector3(0, 0, 0); + } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1091,7 +1137,7 @@ void AnimationTree::_process_graph(double p_delta) { continue; } a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); - t->rot += (rot[1].log() - rot[0].log()) * blend; + t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } } else { @@ -1101,7 +1147,7 @@ void AnimationTree::_process_graph(double p_delta) { continue; } a->rotation_track_interpolate(i, 0, &rot[1]); - t->rot += (rot[1].log() - rot[0].log()) * blend; + t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } } @@ -1112,10 +1158,16 @@ void AnimationTree::_process_graph(double p_delta) { } a->rotation_track_interpolate(i, time, &rot[1]); - t->rot += (rot[1].log() - rot[0].log()) * blend; + t->rot = (t->rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = !backward ? 0 : (double)a->get_length(); } else { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = t->init_loc; + t->rot = t->init_rot; + t->scale = t->init_scale; + } Quaternion rot; Error err = a->rotation_track_interpolate(i, time, &rot); @@ -1123,23 +1175,20 @@ void AnimationTree::_process_graph(double p_delta) { continue; } - if (signbit(rot.dot(t->ref_rot))) { - rot = -rot; - } - t->rot += (rot.log() - t->init_rot) * blend; + t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized(); } #endif // _3D_DISABLED } break; case Animation::TYPE_SCALE_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - if (t->process_pass != process_pass) { - t->process_pass = process_pass; - t->loc = t->init_loc; - t->rot = t->init_rot; - t->scale = t->init_scale; - } if (track->root_motion) { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = Vector3(0, 0, 0); + t->rot = Quaternion(0, 0, 0, 1); + t->scale = Vector3(0, 0, 0); + } double prev_time = time - delta; if (!backward) { if (prev_time < 0) { @@ -1209,6 +1258,12 @@ void AnimationTree::_process_graph(double p_delta) { prev_time = !backward ? 0 : (double)a->get_length(); } else { + if (t->process_pass != process_pass) { + t->process_pass = process_pass; + t->loc = t->init_loc; + t->rot = t->init_rot; + t->scale = t->init_scale; + } Vector3 scale; Error err = a->scale_track_interpolate(i, time, &scale); @@ -1226,7 +1281,7 @@ void AnimationTree::_process_graph(double p_delta) { if (t->process_pass != process_pass) { t->process_pass = process_pass; - t->value = 0; + t->value = t->init_value; } float value; @@ -1238,7 +1293,7 @@ void AnimationTree::_process_graph(double p_delta) { continue; } - t->value += value * blend; + t->value += (value - t->init_value) * blend; #endif // _3D_DISABLED } break; case Animation::TYPE_VALUE: { @@ -1256,10 +1311,14 @@ void AnimationTree::_process_graph(double p_delta) { if (t->process_pass != process_pass) { t->process_pass = process_pass; - t->value = value; - t->value.zero(); + if (!t->init_value) { + t->init_value = value; + t->init_value.zero(); + } + t->value = t->init_value; } + Variant::sub(value, t->init_value, value); Variant::blend(t->value, value, blend, t->value); } else { if (blend < CMP_EPSILON) { @@ -1303,10 +1362,10 @@ void AnimationTree::_process_graph(double p_delta) { if (t->process_pass != process_pass) { t->process_pass = process_pass; - t->value = 0; + t->value = t->init_value; } - t->value += bezier * blend; + t->value += (bezier - t->init_value) * blend; } break; case Animation::TYPE_AUDIO: { if (blend < CMP_EPSILON) { @@ -1505,9 +1564,8 @@ void AnimationTree::_process_graph(double p_delta) { { // finally, set the tracks - const NodePath *K = nullptr; - while ((K = track_cache.next(K))) { - TrackCache *track = track_cache[*K]; + for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + TrackCache *track = K.value; if (track->process_pass != process_pass) { continue; //not processed, ignore } @@ -1516,12 +1574,11 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); - t->rot = t->rot.exp(); if (t->root_motion) { Transform3D xform; xform.origin = t->loc; - xform.basis.set_quaternion_scale(t->rot, t->scale); + xform.basis.set_quaternion_scale(t->rot, Vector3(1, 1, 1) + t->scale); root_motion_transform = xform; diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 3ccb6be073..d40d4ccbbd 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -37,6 +37,8 @@ #include "scene/resources/animation.h" class AnimationNodeBlendTree; +class AnimationNodeStartState; +class AnimationNodeEndState; class AnimationPlayer; class AnimationTree; @@ -164,6 +166,14 @@ public: AnimationRootNode() {} }; +class AnimationNodeStartState : public AnimationRootNode { + GDCLASS(AnimationNodeStartState, AnimationRootNode); +}; + +class AnimationNodeEndState : public AnimationRootNode { + GDCLASS(AnimationNodeEndState, AnimationRootNode); +}; + class AnimationTree : public Node { GDCLASS(AnimationTree, Node); @@ -198,8 +208,7 @@ private: bool rot_used = false; bool scale_used = false; Vector3 init_loc = Vector3(0, 0, 0); - Quaternion ref_rot = Quaternion(0, 0, 0, 1); - Quaternion init_rot = Quaternion(0, 0, 0, 0); + Quaternion init_rot = Quaternion(0, 0, 0, 1); Vector3 init_scale = Vector3(1, 1, 1); Vector3 loc; Quaternion rot; @@ -212,12 +221,14 @@ private: struct TrackCacheBlendShape : public TrackCache { MeshInstance3D *mesh_3d = nullptr; + float init_value = 0; float value = 0; int shape_index = -1; TrackCacheBlendShape() { type = Animation::TYPE_BLEND_SHAPE; } }; struct TrackCacheValue : public TrackCache { + Variant init_value; Variant value; Vector<StringName> subpath; TrackCacheValue() { type = Animation::TYPE_VALUE; } @@ -228,6 +239,7 @@ private: }; struct TrackCacheBezier : public TrackCache { + real_t init_value = 0.0; real_t value = 0.0; Vector<StringName> subpath; TrackCacheBezier() { @@ -254,7 +266,7 @@ private: }; HashMap<NodePath, TrackCache *> track_cache; - Set<TrackCache *> playing_caches; + RBSet<TrackCache *> playing_caches; Ref<AnimationNode> root; diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index 42adc1ea02..3192f5f7cd 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -114,9 +114,8 @@ void RootMotionView::_notification(int p_what) { first = false; transform.orthonormalize(); //don't want scale, too imprecise - transform.affine_invert(); - accumulated = transform * accumulated; + accumulated = accumulated * transform; accumulated.origin.x = Math::fposmod(accumulated.origin.x, cell_size); if (zero_y) { accumulated.origin.y = 0; @@ -134,9 +133,9 @@ void RootMotionView::_notification(int p_what) { Vector3 from(i * cell_size, 0, j * cell_size); Vector3 from_i((i + 1) * cell_size, 0, j * cell_size); Vector3 from_j(i * cell_size, 0, (j + 1) * cell_size); - from = accumulated.xform(from); - from_i = accumulated.xform(from_i); - from_j = accumulated.xform(from_j); + from = accumulated.xform_inv(from); + from_i = accumulated.xform_inv(from_i); + from_j = accumulated.xform_inv(from_j); Color c = color, c_i = color, c_j = color; c.a *= MAX(0, 1.0 - from.length() / radius); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index c8eb270a0a..5457da472f 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -130,6 +130,7 @@ void Tween::stop() { started = false; running = false; dead = false; + total_time = 0; } void Tween::pause() { @@ -151,10 +152,6 @@ bool Tween::is_running() { return running; } -void Tween::set_valid(bool p_valid) { - valid = p_valid; -} - bool Tween::is_valid() { return valid; } @@ -272,21 +269,24 @@ bool Tween::step(float p_delta) { ERR_FAIL_COND_V_MSG(tweeners.is_empty(), false, "Tween started, but has no Tweeners."); current_step = 0; loops_done = 0; + total_time = 0; start_tweeners(); started = true; } float rem_delta = p_delta * speed_scale; bool step_active = false; + total_time += rem_delta; + +#ifdef DEBUG_ENABLED + float initial_delta = rem_delta; + bool potential_infinite = false; +#endif while (rem_delta > 0 && running) { float step_delta = rem_delta; step_active = false; -#ifdef DEBUG_ENABLED - float prev_delta = rem_delta; -#endif - for (Ref<Tweener> &tweener : tweeners.write[current_step]) { // Modified inside Tweener.step(). float temp_delta = rem_delta; @@ -311,17 +311,21 @@ bool Tween::step(float p_delta) { emit_signal(SNAME("loop_finished"), loops_done); current_step = 0; start_tweeners(); +#ifdef DEBUG_ENABLED + if (loops <= 0 && Math::is_equal_approx(rem_delta, initial_delta)) { + if (!potential_infinite) { + potential_infinite = true; + } else { + // Looped twice without using any time, this is 100% certain infinite loop. + ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info."); + } + } +#endif } } else { start_tweeners(); } } - -#ifdef DEBUG_ENABLED - if (Math::is_equal_approx(rem_delta, prev_delta) && running && loops <= 0) { - ERR_FAIL_V_MSG(false, "Infinite loop detected. Check set_loops() description for more info."); - } -#endif } return true; @@ -331,7 +335,7 @@ bool Tween::can_process(bool p_tree_paused) const { if (is_bound && pause_mode == TWEEN_PAUSE_BOUND) { Node *bound_node = get_bound_node(); if (bound_node) { - return bound_node->can_process(); + return bound_node->is_inside_tree() && bound_node->can_process(); } } @@ -346,6 +350,10 @@ Node *Tween::get_bound_node() const { } } +float Tween::get_total_time() const { + return total_time; +} + real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) { if (p_duration == 0) { // Special case to avoid dividing by 0 in equations. @@ -448,12 +456,12 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f Transform2D d = p_delta_val; Transform2D r; - APPLY_EQUATION(elements[0][0]); - APPLY_EQUATION(elements[0][1]); - APPLY_EQUATION(elements[1][0]); - APPLY_EQUATION(elements[1][1]); - APPLY_EQUATION(elements[2][0]); - APPLY_EQUATION(elements[2][1]); + APPLY_EQUATION(columns[0][0]); + APPLY_EQUATION(columns[0][1]); + APPLY_EQUATION(columns[1][0]); + APPLY_EQUATION(columns[1][1]); + APPLY_EQUATION(columns[2][0]); + APPLY_EQUATION(columns[2][1]); return r; } @@ -488,15 +496,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f Basis d = p_delta_val; Basis r; - APPLY_EQUATION(elements[0][0]); - APPLY_EQUATION(elements[0][1]); - APPLY_EQUATION(elements[0][2]); - APPLY_EQUATION(elements[1][0]); - APPLY_EQUATION(elements[1][1]); - APPLY_EQUATION(elements[1][2]); - APPLY_EQUATION(elements[2][0]); - APPLY_EQUATION(elements[2][1]); - APPLY_EQUATION(elements[2][2]); + APPLY_EQUATION(rows[0][0]); + APPLY_EQUATION(rows[0][1]); + APPLY_EQUATION(rows[0][2]); + APPLY_EQUATION(rows[1][0]); + APPLY_EQUATION(rows[1][1]); + APPLY_EQUATION(rows[1][2]); + APPLY_EQUATION(rows[2][0]); + APPLY_EQUATION(rows[2][1]); + APPLY_EQUATION(rows[2][2]); return r; } @@ -505,15 +513,15 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f Transform3D d = p_delta_val; Transform3D r; - APPLY_EQUATION(basis.elements[0][0]); - APPLY_EQUATION(basis.elements[0][1]); - APPLY_EQUATION(basis.elements[0][2]); - APPLY_EQUATION(basis.elements[1][0]); - APPLY_EQUATION(basis.elements[1][1]); - APPLY_EQUATION(basis.elements[1][2]); - APPLY_EQUATION(basis.elements[2][0]); - APPLY_EQUATION(basis.elements[2][1]); - APPLY_EQUATION(basis.elements[2][2]); + APPLY_EQUATION(basis.rows[0][0]); + APPLY_EQUATION(basis.rows[0][1]); + APPLY_EQUATION(basis.rows[0][2]); + APPLY_EQUATION(basis.rows[1][0]); + APPLY_EQUATION(basis.rows[1][1]); + APPLY_EQUATION(basis.rows[1][2]); + APPLY_EQUATION(basis.rows[2][0]); + APPLY_EQUATION(basis.rows[2][1]); + APPLY_EQUATION(basis.rows[2][2]); APPLY_EQUATION(origin.x); APPLY_EQUATION(origin.y); APPLY_EQUATION(origin.z); @@ -562,12 +570,12 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) case Variant::TRANSFORM2D: { Transform2D i = p_intial_val; Transform2D f = p_final_val; - return Transform2D(f.elements[0][0] - i.elements[0][0], - f.elements[0][1] - i.elements[0][1], - f.elements[1][0] - i.elements[1][0], - f.elements[1][1] - i.elements[1][1], - f.elements[2][0] - i.elements[2][0], - f.elements[2][1] - i.elements[2][1]); + return Transform2D(f.columns[0][0] - i.columns[0][0], + f.columns[0][1] - i.columns[0][1], + f.columns[1][0] - i.columns[1][0], + f.columns[1][1] - i.columns[1][1], + f.columns[2][0] - i.columns[2][0], + f.columns[2][1] - i.columns[2][1]); } case Variant::AABB: { @@ -579,29 +587,29 @@ Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) case Variant::BASIS: { Basis i = p_intial_val; Basis f = p_final_val; - return Basis(f.elements[0][0] - i.elements[0][0], - f.elements[0][1] - i.elements[0][1], - f.elements[0][2] - i.elements[0][2], - f.elements[1][0] - i.elements[1][0], - f.elements[1][1] - i.elements[1][1], - f.elements[1][2] - i.elements[1][2], - f.elements[2][0] - i.elements[2][0], - f.elements[2][1] - i.elements[2][1], - f.elements[2][2] - i.elements[2][2]); + return Basis(f.rows[0][0] - i.rows[0][0], + f.rows[0][1] - i.rows[0][1], + f.rows[0][2] - i.rows[0][2], + f.rows[1][0] - i.rows[1][0], + f.rows[1][1] - i.rows[1][1], + f.rows[1][2] - i.rows[1][2], + f.rows[2][0] - i.rows[2][0], + f.rows[2][1] - i.rows[2][1], + f.rows[2][2] - i.rows[2][2]); } case Variant::TRANSFORM3D: { Transform3D i = p_intial_val; Transform3D f = p_final_val; - return Transform3D(f.basis.elements[0][0] - i.basis.elements[0][0], - f.basis.elements[0][1] - i.basis.elements[0][1], - f.basis.elements[0][2] - i.basis.elements[0][2], - f.basis.elements[1][0] - i.basis.elements[1][0], - f.basis.elements[1][1] - i.basis.elements[1][1], - f.basis.elements[1][2] - i.basis.elements[1][2], - f.basis.elements[2][0] - i.basis.elements[2][0], - f.basis.elements[2][1] - i.basis.elements[2][1], - f.basis.elements[2][2] - i.basis.elements[2][2], + return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0], + f.basis.rows[0][1] - i.basis.rows[0][1], + f.basis.rows[0][2] - i.basis.rows[0][2], + f.basis.rows[1][0] - i.basis.rows[1][0], + f.basis.rows[1][1] - i.basis.rows[1][1], + f.basis.rows[1][2] - i.basis.rows[1][2], + f.basis.rows[2][0] - i.basis.rows[2][0], + f.basis.rows[2][1] - i.basis.rows[2][1], + f.basis.rows[2][2] - i.basis.rows[2][2], f.origin.x - i.origin.x, f.origin.y - i.origin.y, f.origin.z - i.origin.z); @@ -624,6 +632,7 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("pause"), &Tween::pause); ClassDB::bind_method(D_METHOD("play"), &Tween::play); ClassDB::bind_method(D_METHOD("kill"), &Tween::kill); + ClassDB::bind_method(D_METHOD("get_total_elapsed_time"), &Tween::get_total_time); ClassDB::bind_method(D_METHOD("is_running"), &Tween::is_running); ClassDB::bind_method(D_METHOD("is_valid"), &Tween::is_valid); @@ -640,7 +649,7 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("parallel"), &Tween::parallel); ClassDB::bind_method(D_METHOD("chain"), &Tween::chain); - ClassDB::bind_method(D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant); + ClassDB::bind_static_method("Tween", D_METHOD("interpolate_value", "initial_value", "delta_value", "elapsed_time", "duration", "trans_type", "ease_type"), &Tween::interpolate_variant); ADD_SIGNAL(MethodInfo("step_finished", PropertyInfo(Variant::INT, "idx"))); ADD_SIGNAL(MethodInfo("loop_finished", PropertyInfo(Variant::INT, "loop_count"))); @@ -671,6 +680,14 @@ void Tween::_bind_methods() { BIND_ENUM_CONSTANT(EASE_OUT_IN); } +Tween::Tween() { + ERR_FAIL_MSG("Tween can't be created directly. Use create_tween() method."); +} + +Tween::Tween(bool p_valid) { + valid = p_valid; +} + Ref<PropertyTweener> PropertyTweener::from(Variant p_value) { initial_val = p_value; do_continue = false; @@ -838,7 +855,7 @@ bool CallbackTweener::step(float &r_delta) { Callable::CallError ce; callback.call(nullptr, 0, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_call_error_text(this, callback.get_method(), nullptr, 0, ce)); + ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce)); } finished = true; @@ -909,7 +926,7 @@ bool MethodTweener::step(float &r_delta) { Callable::CallError ce; callback.call(argptr, 1, result, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_call_error_text(this, callback.get_method(), argptr, 1, ce)); + ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce)); } if (time < duration) { diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 62c357dfb4..40268405cf 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -103,6 +103,7 @@ private: ObjectID bound_node; Vector<List<Ref<Tweener>>> tweeners; + float total_time = 0; int current_step = -1; int loops = 1; int loops_done = 0; @@ -115,6 +116,9 @@ private: bool valid = false; bool default_parallel = false; bool parallel_enabled = false; +#ifdef DEBUG_ENABLED + bool is_infinite = false; +#endif typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d); static interpolater interpolaters[TRANS_MAX][EASE_MAX]; @@ -138,7 +142,6 @@ public: void kill(); bool is_running(); - void set_valid(bool p_valid); bool is_valid(); void clear(); @@ -159,15 +162,17 @@ public: Ref<Tween> parallel(); Ref<Tween> chain(); - real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); - Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); + static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); + static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val); bool step(float p_delta); bool can_process(bool p_tree_paused) const; Node *get_bound_node() const; + float get_total_time() const; - Tween() {} + Tween(); + Tween(bool p_valid); }; VARIANT_ENUM_CAST(Tween::TweenPauseMode); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index ac0d017a23..b792b11dbc 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) { @@ -100,7 +100,7 @@ public: } } - void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { + void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { uint64_t pt = OS::get_singleton()->get_ticks_msec(); if (pt - last_profile_time > 100) { last_profile_time = pt; @@ -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 (RBSet<StringName>::Element *E = sm.value.front(); E; E = E->next()) { Variant m; if (p_instance->get(E->get(), m)) { - String script_path = sm->key() == p_script ? "" : sm->key()->get_path().get_file() + "/"; + String script_path = sm.key == p_script ? "" : sm.key->get_path().get_file() + "/"; PropertyInfo pi(m.get_type(), "Members/" + script_path + E->get()); 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"); @@ -467,7 +467,7 @@ void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) { const PropertyInfo &pi = properties[i].first; Variant &var = properties[i].second; - RES res = var; + Ref<Resource> res = var; Array prop; prop.push_back(pi.name); @@ -529,7 +529,7 @@ void SceneDebuggerObject::deserialize(const Array &p_arr) { if (pinfo.type == Variant::OBJECT) { if (var.is_zero()) { - var = RES(); + var = Ref<Resource>(); } else if (var.get_type() == Variant::OBJECT) { if (((Object *)var)->is_class("EncodedObjectAsID")) { var = Object::cast_to<EncodedObjectAsID>(var)->get_object_id(); @@ -624,12 +624,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { @@ -646,7 +646,7 @@ void LiveEditor::_node_set_func(int p_id, const StringName &p_prop, const Varian } void LiveEditor::_node_set_res_func(int p_id, const StringName &p_prop, const String &p_value) { - RES r = ResourceLoader::load(p_value); + Ref<Resource> r = ResourceLoader::load(p_value); if (!r.is_valid()) { return; } @@ -668,12 +668,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { @@ -701,7 +701,7 @@ void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant return; } - RES r = ResourceCache::get(resp); + Ref<Resource> r = ResourceCache::get(resp); if (!r.is_valid()) { return; } @@ -710,7 +710,7 @@ void LiveEditor::_res_set_func(int p_id, const StringName &p_prop, const Variant } void LiveEditor::_res_set_res_func(int p_id, const StringName &p_prop, const String &p_value) { - RES r = ResourceLoader::load(p_value); + Ref<Resource> r = ResourceLoader::load(p_value); if (!r.is_valid()) { return; } @@ -728,7 +728,7 @@ void LiveEditor::_res_call_func(int p_id, const StringName &p_method, const Vari return; } - RES r = ResourceCache::get(resp); + Ref<Resource> r = ResourceCache::get(resp); if (!r.is_valid()) { return; } @@ -753,12 +753,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { @@ -797,12 +797,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { @@ -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,12 +963,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { @@ -1002,12 +1002,12 @@ 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()) { + for (RBSet<Node *>::Element *F = E->value.front(); F; F = F->next()) { Node *n = F->get(); if (base && !base->is_ancestor_of(n)) { 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/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index b59eda465e..75f19ac452 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -172,7 +172,7 @@ void AspectRatioContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alignment_vertical", "alignment_vertical"), &AspectRatioContainer::set_alignment_vertical); ClassDB::bind_method(D_METHOD("get_alignment_vertical"), &AspectRatioContainer::get_alignment_vertical); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio"), "set_ratio", "get_ratio"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio", PROPERTY_HINT_RANGE, "0.001,10.0,0.0001,or_greater"), "set_ratio", "get_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width Controls Height,Height Controls Width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode"); ADD_GROUP("Alignment", "alignment_"); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index ab86face7e..595f0cbea7 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -43,7 +43,7 @@ void BaseButton::_unpress_group() { status.pressed = true; } - for (Set<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) { + for (RBSet<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) { if (E->get() == this) { continue; } @@ -339,14 +339,14 @@ bool BaseButton::is_keep_pressed_outside() const { void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) { shortcut = p_shortcut; - set_process_unhandled_key_input(shortcut.is_valid()); + set_process_shortcut_input(shortcut.is_valid()); } Ref<Shortcut> BaseButton::get_shortcut() const { return shortcut; } -void BaseButton::unhandled_key_input(const Ref<InputEvent> &p_event) { +void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!_is_focus_owner_in_shortcut_context()) { @@ -485,14 +485,14 @@ BaseButton::~BaseButton() { } void ButtonGroup::get_buttons(List<BaseButton *> *r_buttons) { - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { + for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { r_buttons->push_back(E->get()); } } Array ButtonGroup::_get_buttons() { Array btns; - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { + for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { btns.push_back(E->get()); } @@ -500,7 +500,7 @@ Array ButtonGroup::_get_buttons() { } BaseButton *ButtonGroup::get_pressed_button() { - for (Set<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { + for (RBSet<BaseButton *>::Element *E = buttons.front(); E; E = E->next()) { if (E->get()->is_pressed()) { return E->get(); } diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index a2b6ee0845..0b70d285ee 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -77,7 +77,7 @@ protected: virtual void toggled(bool p_pressed); static void _bind_methods(); virtual void gui_input(const Ref<InputEvent> &p_event) override; - virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; + virtual void shortcut_input(const Ref<InputEvent> &p_event) override; void _notification(int p_what); bool _is_focus_owner_in_shortcut_context() const; @@ -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 29a0681f9c..ff194f979d 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -53,7 +53,7 @@ Size2 Button::get_minimum_size() const { if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) { minsize.width += _icon->get_width(); if (!xl_text.is_empty()) { - minsize.width += get_theme_constant(SNAME("hseparation")); + minsize.width += get_theme_constant(SNAME("h_separation")); } } else { minsize.width = MAX(minsize.width, _icon->get_width()); @@ -244,21 +244,22 @@ void Button::_notification(int p_what) { if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) { style_offset.x = style->get_margin(SIDE_LEFT); if (_internal_margin[SIDE_LEFT] > 0) { - icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation")); + icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); } } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { style_offset.x = 0.0; } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) { style_offset.x = -style->get_margin(SIDE_RIGHT); if (_internal_margin[SIDE_RIGHT] > 0) { - icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation")); + icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); } } style_offset.y = style->get_margin(SIDE_TOP); if (expand_icon) { Size2 _size = get_size() - style->get_offset() * 2; - _size.width -= get_theme_constant(SNAME("hseparation")) + icon_ofs_region; + int icon_text_separation = text.is_empty() ? 0 : get_theme_constant(SNAME("h_separation")); + _size.width -= icon_text_separation + icon_ofs_region; if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) { _size.width -= text_buf->get_size().width; } @@ -286,7 +287,7 @@ void Button::_notification(int p_what) { } } - Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("hseparation")), 0) : Point2(); + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("h_separation")), 0) : Point2(); if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) { icon_ofs.x = 0.0; } @@ -296,10 +297,10 @@ void Button::_notification(int p_what) { int text_width = MAX(1, clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x); if (_internal_margin[SIDE_LEFT] > 0) { - text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation")); + text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); } if (_internal_margin[SIDE_RIGHT] > 0) { - text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("hseparation")); + text_clip -= _internal_margin[SIDE_RIGHT] + get_theme_constant(SNAME("h_separation")); } Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0; @@ -313,7 +314,7 @@ void Button::_notification(int p_what) { icon_ofs.x = 0.0; } if (_internal_margin[SIDE_LEFT] > 0) { - text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation")); + text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("h_separation")); } else { text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x; } @@ -330,7 +331,7 @@ void Button::_notification(int p_what) { } break; case HORIZONTAL_ALIGNMENT_RIGHT: { if (_internal_margin[SIDE_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation")); + text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("h_separation")); } else { text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width; } diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index 063a154bb2..cb80f5b5ef 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -75,7 +75,7 @@ Size2 CheckBox::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += get_theme_constant(SNAME("hseparation")); + minsize.width += get_theme_constant(SNAME("h_separation")); } Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); @@ -110,7 +110,7 @@ void CheckBox::_notification(int p_what) { } else { ofs.x = sb->get_margin(SIDE_LEFT); } - ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust")); + ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_v_adjust")); if (is_pressed()) { on->draw(ci, ofs); diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index 527b0061ac..a09873ea4f 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -52,7 +52,7 @@ Size2 CheckButton::get_minimum_size() const { Size2 tex_size = get_icon_size(); minsize.width += tex_size.width; if (get_text().length() > 0) { - minsize.width += get_theme_constant(SNAME("hseparation")); + minsize.width += get_theme_constant(SNAME("h_separation")); } Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(SIDE_TOP) + sb->get_margin(SIDE_BOTTOM)); @@ -100,7 +100,7 @@ void CheckButton::_notification(int p_what) { } else { ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); } - ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust")); + ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_v_adjust")); if (is_pressed()) { on->draw(ci, ofs); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 2e87e71972..197c9005c3 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -106,7 +106,7 @@ void CodeEdit::_notification(int p_what) { const int code_completion_options_count = code_completion_options.size(); const int lines = MIN(code_completion_options_count, code_completion_max_lines); - const int icon_hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); + const int icon_hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList")); const Size2 icon_area_size(row_height, row_height); code_completion_rect.size.width = code_completion_longest_line + icon_hsep + icon_area_size.width + 2; @@ -357,6 +357,11 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } Ref<InputEventKey> k = p_gui_input; + if (TextEdit::alt_input(p_gui_input)) { + accept_event(); + return; + } + bool update_code_completion = false; if (!k.is_valid()) { TextEdit::gui_input(p_gui_input); @@ -735,7 +740,7 @@ 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()) { + for (const RBSet<char32_t>::Element *E = auto_indent_prefixes.front(); E; E = E->next()) { prefixes.push_back(String::chr(E->get())); } return prefixes; @@ -994,7 +999,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { } /* Make sure this is the last char, trailing whitespace or comments are okay. */ - if (should_indent && (!is_whitespace(c) && is_in_comment(cl, cc) == -1)) { + /* Increment column for comments because the delimiter (#) should be ignored. */ + if (should_indent && (!is_whitespace(c) && is_in_comment(cl, line_col + 1) == -1)) { should_indent = false; } } @@ -1622,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]) { @@ -1746,7 +1752,7 @@ 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()) { + for (const RBSet<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) { prefixes.push_back(String::chr(E->get())); } return prefixes; @@ -1817,7 +1823,7 @@ void CodeEdit::request_code_completion(bool p_force) { } } -void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const RES &p_icon, const Variant &p_value) { +void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value) { ScriptLanguage::CodeCompletionOption completion_option; completion_option.kind = (ScriptLanguage::CodeCompletionKind)p_type; completion_option.display = p_display_text; @@ -2195,7 +2201,7 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text_for_code_completion"), &CodeEdit::get_text_for_code_completion); ClassDB::bind_method(D_METHOD("request_code_completion", "force"), &CodeEdit::request_code_completion, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(RES()), DEFVAL(Variant::NIL)); + ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL)); ClassDB::bind_method(D_METHOD("update_code_completion_options", "force"), &CodeEdit::update_code_completion_options); ClassDB::bind_method(D_METHOD("get_code_completion_options"), &CodeEdit::get_code_completion_options); ClassDB::bind_method(D_METHOD("get_code_completion_option", "index"), &CodeEdit::get_code_completion_option); @@ -2396,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>()); } } } @@ -2533,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) { @@ -3035,7 +3041,9 @@ void CodeEdit::_text_changed() { lc = get_line_count(); List<int> breakpoints; - breakpointed_lines.get_key_list(&breakpoints); + for (const KeyValue<int, bool> &E : breakpointed_lines) { + breakpoints.push_back(E.key); + } for (const int &line : breakpoints) { if (line < lines_edited_from || (line < lc && is_line_breakpointed(line))) { continue; diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 596a065f12..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; @@ -398,7 +398,7 @@ public: void request_code_completion(bool p_force = false); - void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const RES &p_icon = RES(), const Variant &p_value = Variant::NIL); + void add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color = Color(1, 1, 1), const Ref<Resource> &p_icon = Ref<Resource>(), const Variant &p_value = Variant::NIL); void update_code_completion_options(bool p_forced = false); TypedArray<Dictionary> get_code_completion_options() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 48fadb0cf8..6f7ad94139 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -436,7 +436,7 @@ ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const { } inline int ColorPicker::_get_preset_size() { - return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("hseparation")) * (preset_column_count - 1))) / preset_column_count; + return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("h_separation")) * (preset_column_count - 1))) / preset_column_count; } void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 96d2b29fc1..54fa726260 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1501,14 +1501,15 @@ void Control::_set_layout_mode(LayoutMode p_mode) { bool list_changed = false; if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) { - if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)p_mode) { + if ((int)get_meta("_edit_layout_mode", p_mode) != (int)p_mode) { list_changed = true; } set_meta("_edit_layout_mode", (int)p_mode); if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) { - set_meta("_edit_use_custom_anchors", false); + remove_meta("_edit_layout_mode"); + remove_meta("_edit_use_custom_anchors"); set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT); } @@ -1589,22 +1590,22 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, void Control::_set_anchors_layout_preset(int p_preset) { bool list_changed = false; - if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)LayoutMode::LAYOUT_MODE_ANCHORS) { + if (get_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS).operator int() != LayoutMode::LAYOUT_MODE_ANCHORS) { list_changed = true; - set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS); + set_meta("_edit_layout_mode", LayoutMode::LAYOUT_MODE_ANCHORS); } if (p_preset == -1) { - if (!has_meta("_edit_use_custom_anchors") || !(bool)get_meta("_edit_use_custom_anchors")) { + if (!get_meta("_edit_use_custom_anchors", false)) { set_meta("_edit_use_custom_anchors", true); notify_property_list_changed(); } return; // Keep settings as is. } - if (!has_meta("_edit_use_custom_anchors") || (bool)get_meta("_edit_use_custom_anchors")) { + if (get_meta("_edit_use_custom_anchors", true)) { list_changed = true; - set_meta("_edit_use_custom_anchors", false); + remove_meta("_edit_use_custom_anchors"); } LayoutPreset preset = (LayoutPreset)p_preset; @@ -1645,7 +1646,7 @@ void Control::_set_anchors_layout_preset(int p_preset) { int Control::_get_anchors_layout_preset() const { // If the custom preset was selected by user, use it. - if (has_meta("_edit_use_custom_anchors") && (bool)get_meta("_edit_use_custom_anchors")) { + if ((bool)get_meta("_edit_use_custom_anchors", false)) { return -1; } @@ -2070,7 +2071,7 @@ Point2 Control::get_global_position() const { Point2 Control::get_screen_position() const { ERR_FAIL_COND_V(!is_inside_tree(), Point2()); - Point2 global_pos = get_viewport()->get_canvas_transform().xform(get_global_position()); + Point2 global_pos = get_global_transform_with_canvas().get_origin(); Window *w = Object::cast_to<Window>(get_viewport()); if (w && !w->is_embedding_subwindows()) { global_pos += w->get_position(); @@ -2946,90 +2947,17 @@ bool Control::is_text_field() const { return false; } -Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const { - Array ret; - switch (p_theme_type) { - case STRUCTURED_TEXT_URI: { - int prev = 0; - for (int i = 0; i < p_text.length(); i++) { - if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) { - if (prev != i) { - ret.push_back(Vector2i(prev, i)); - } - ret.push_back(Vector2i(i, i + 1)); - prev = i + 1; - } - } - if (prev != p_text.length()) { - ret.push_back(Vector2i(prev, p_text.length())); - } - } break; - case STRUCTURED_TEXT_FILE: { - int prev = 0; - for (int i = 0; i < p_text.length(); i++) { - if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) { - if (prev != i) { - ret.push_back(Vector2i(prev, i)); - } - ret.push_back(Vector2i(i, i + 1)); - prev = i + 1; - } - } - if (prev != p_text.length()) { - ret.push_back(Vector2i(prev, p_text.length())); - } - } break; - case STRUCTURED_TEXT_EMAIL: { - bool local = true; - int prev = 0; - for (int i = 0; i < p_text.length(); i++) { - if ((p_text[i] == '@') && local) { // Add full "local" as single context. - local = false; - ret.push_back(Vector2i(prev, i)); - ret.push_back(Vector2i(i, i + 1)); - prev = i + 1; - } else if (!local & (p_text[i] == '.')) { // Add each dot separated "domain" part as context. - if (prev != i) { - ret.push_back(Vector2i(prev, i)); - } - ret.push_back(Vector2i(i, i + 1)); - prev = i + 1; - } - } - if (prev != p_text.length()) { - ret.push_back(Vector2i(prev, p_text.length())); - } - } break; - case STRUCTURED_TEXT_LIST: { - if (p_args.size() == 1 && p_args[0].get_type() == Variant::STRING) { - Vector<String> tags = p_text.split(String(p_args[0])); - int prev = 0; - for (int i = 0; i < tags.size(); i++) { - if (prev != i) { - ret.push_back(Vector2i(prev, prev + tags[i].length())); - } - ret.push_back(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1)); - prev = prev + tags[i].length() + 1; - } - } - } break; - case STRUCTURED_TEXT_CUSTOM: { - Array r; - if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, r)) { - for (int i = 0; i < r.size(); i++) { - if (r[i].get_type() == Variant::VECTOR2I) { - ret.push_back(Vector2i(r[i])); - } - } - } - } break; - case STRUCTURED_TEXT_NONE: - case STRUCTURED_TEXT_DEFAULT: - default: { - ret.push_back(Vector2i(0, p_text.length())); +Array Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { + if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { + Array ret; + if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) { + return ret; + } else { + return Array(); } + } else { + return TS->parse_structured_text(p_parser_type, p_args, p_text); } - return ret; } void Control::set_rotation(real_t p_radians) { @@ -3491,14 +3419,6 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(TEXT_DIRECTION_LTR); BIND_ENUM_CONSTANT(TEXT_DIRECTION_RTL); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_DEFAULT); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_URI); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE); - BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM); - ADD_SIGNAL(MethodInfo("resized")); ADD_SIGNAL(MethodInfo("gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ADD_SIGNAL(MethodInfo("mouse_entered")); diff --git a/scene/gui/control.h b/scene/gui/control.h index 4240d52b65..65b71d74f8 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -147,16 +147,6 @@ public: TEXT_DIRECTION_INHERITED, }; - enum StructuredTextParser { - STRUCTURED_TEXT_DEFAULT, - STRUCTURED_TEXT_URI, - STRUCTURED_TEXT_FILE, - STRUCTURED_TEXT_EMAIL, - STRUCTURED_TEXT_LIST, - STRUCTURED_TEXT_NONE, - STRUCTURED_TEXT_CUSTOM - }; - private: struct CComparator { bool operator()(const Control *p_a, const Control *p_b) const { @@ -290,7 +280,7 @@ protected: //virtual void _window_gui_input(InputEvent p_event); - virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const; + virtual Array structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -579,6 +569,5 @@ VARIANT_ENUM_CAST(Control::Anchor); VARIANT_ENUM_CAST(Control::LayoutMode); VARIANT_ENUM_CAST(Control::LayoutDirection); VARIANT_ENUM_CAST(Control::TextDirection); -VARIANT_ENUM_CAST(Control::StructuredTextParser); #endif diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index 11c701b0d5..41fd9c0a10 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -45,10 +45,10 @@ class AcceptDialog : public Window { GDCLASS(AcceptDialog, Window); Window *parent_visible = nullptr; - Panel *bg; - HBoxContainer *hbc; - Label *label; - Button *ok; + Panel *bg = nullptr; + HBoxContainer *hbc = nullptr; + Label *label = nullptr; + Button *ok = nullptr; bool hide_on_ok = true; bool close_on_escape = true; @@ -103,7 +103,7 @@ public: class ConfirmationDialog : public AcceptDialog { GDCLASS(ConfirmationDialog, AcceptDialog); - Button *cancel; + Button *cancel = nullptr; protected: static void _bind_methods(); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index e71ab64535..1725816c31 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -95,7 +95,7 @@ void FileDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { - set_process_unhandled_input(false); + set_process_shortcut_input(false); } } break; @@ -119,7 +119,7 @@ void FileDialog::_notification(int p_what) { } } -void FileDialog::unhandled_input(const Ref<InputEvent> &p_event) { +void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref<InputEventKey> k = p_event; @@ -217,7 +217,7 @@ void FileDialog::_post_popup() { tree->grab_focus(); } - set_process_unhandled_input(true); + set_process_shortcut_input(true); // For open dir mode, deselect all items on file dialog open. if (mode == FILE_MODE_OPEN_DIR) { @@ -798,7 +798,6 @@ void FileDialog::set_access(Access p_access) { if (access == p_access) { return; } - memdelete(dir_access); switch (p_access) { case ACCESS_FILESYSTEM: { dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -916,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"); @@ -989,7 +988,7 @@ FileDialog::FileDialog() { hbc->add_child(drives); dir = memnew(LineEdit); - dir->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); + dir->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); hbc->add_child(dir); dir->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -1030,7 +1029,7 @@ FileDialog::FileDialog() { file_box = memnew(HBoxContainer); file_box->add_child(memnew(Label(TTRC("File:")))); file = memnew(LineEdit); - file->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); + file->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); file->set_stretch_ratio(4); file->set_h_size_flags(Control::SIZE_EXPAND_FILL); file_box->add_child(file); @@ -1064,7 +1063,7 @@ FileDialog::FileDialog() { makedialog->add_child(makevb); makedirname = memnew(LineEdit); - makedirname->set_structured_text_bidi_override(Control::STRUCTURED_TEXT_FILE); + makedirname->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE); makevb->add_margin_child(TTRC("Name:"), makedirname); add_child(makedialog, false, INTERNAL_MODE_FRONT); makedialog->register_text_enter(makedirname); @@ -1091,5 +1090,4 @@ FileDialog::~FileDialog() { if (unregister_func) { unregister_func(this); } - memdelete(dir_access); } diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 7a50efe40f..2e326d2949 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -65,34 +65,34 @@ public: static RegisterFunc unregister_func; private: - ConfirmationDialog *makedialog; - LineEdit *makedirname; + ConfirmationDialog *makedialog = nullptr; + LineEdit *makedirname = nullptr; - Button *makedir; + Button *makedir = nullptr; Access access = ACCESS_RESOURCES; - VBoxContainer *vbox; + VBoxContainer *vbox = nullptr; FileMode mode; - LineEdit *dir; - HBoxContainer *drives_container; - HBoxContainer *shortcuts_container; - OptionButton *drives; - Tree *tree; - HBoxContainer *file_box; - LineEdit *file; - OptionButton *filter; - AcceptDialog *mkdirerr; - AcceptDialog *exterr; - DirAccess *dir_access; - ConfirmationDialog *confirm_save; - - Label *message; - - Button *dir_prev; - Button *dir_next; - Button *dir_up; - - Button *refresh; - Button *show_hidden; + LineEdit *dir = nullptr; + HBoxContainer *drives_container = nullptr; + HBoxContainer *shortcuts_container = nullptr; + OptionButton *drives = nullptr; + Tree *tree = nullptr; + HBoxContainer *file_box = nullptr; + LineEdit *file = nullptr; + OptionButton *filter = nullptr; + AcceptDialog *mkdirerr = nullptr; + AcceptDialog *exterr = nullptr; + Ref<DirAccess> dir_access; + ConfirmationDialog *confirm_save = nullptr; + + Label *message = nullptr; + + Button *dir_prev = nullptr; + Button *dir_next = nullptr; + Button *dir_up = nullptr; + + Button *refresh = nullptr; + Button *show_hidden = nullptr; Vector<String> filters; @@ -133,7 +133,7 @@ private: void _update_drives(bool p_select = true); - virtual void unhandled_input(const Ref<InputEvent> &p_event) override; + virtual void shortcut_input(const Ref<InputEvent> &p_event) override; bool _is_open_should_be_disabled(); diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 40aca555db..30b694da76 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -44,12 +44,12 @@ void FlowContainer::_resort() { return; } - int separation_horizontal = get_theme_constant(SNAME("hseparation")); - int separation_vertical = get_theme_constant(SNAME("vseparation")); + int separation_horizontal = get_theme_constant(SNAME("h_separation")); + int separation_vertical = get_theme_constant(SNAME("v_separation")); 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/gradient_edit.h b/scene/gui/gradient_edit.h index 67531d4f4a..4e3c6525f9 100644 --- a/scene/gui/gradient_edit.h +++ b/scene/gui/gradient_edit.h @@ -38,8 +38,8 @@ class GradientEdit : public Control { GDCLASS(GradientEdit, Control); - PopupPanel *popup; - ColorPicker *picker; + PopupPanel *popup = nullptr; + ColorPicker *picker = nullptr; bool grabbing = false; int grabbed = -1; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 1394b4192f..6f3a361e82 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -523,7 +523,7 @@ void GraphEdit::_update_comment_enclosed_nodes_list(GraphNode *p_node, HashMap<S } } - p_comment_enclosed_nodes.set(p_node->get_name(), enclosed_nodes); + p_comment_enclosed_nodes.insert(p_node->get_name(), enclosed_nodes); } void GraphEdit::_set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag) { @@ -696,7 +696,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_output_hotzone(gn, j, mpos, port_size)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -708,7 +708,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(connecting_type, type))) && is_in_input_hotzone(gn, j, mpos, port_size)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -981,7 +981,7 @@ void GraphEdit::_minimap_draw() { Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate(); // Override default values with colors provided by the GraphNode's stylebox, if possible. - Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "commentfocus" : "comment"); + Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "comment_focus" : "comment"); if (sbf.is_valid()) { Color node_color = sbf->get_bg_color(); sb_minimap->set_bg_color(node_color); @@ -1004,7 +1004,7 @@ void GraphEdit::_minimap_draw() { Ref<StyleBoxFlat> sb_minimap = minimap->get_theme_stylebox(SNAME("node"))->duplicate(); // Override default values with colors provided by the GraphNode's stylebox, if possible. - Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selectedframe" : "frame"); + Ref<StyleBoxFlat> sbf = gn->get_theme_stylebox(gn->is_selected() ? "selected_frame" : "frame"); if (sbf.is_valid()) { Color node_color = sbf->get_border_color(); sb_minimap->set_bg_color(node_color); @@ -1568,26 +1568,17 @@ void GraphEdit::_update_zoom_label() { } void GraphEdit::add_valid_connection_type(int p_type, int p_with_type) { - ConnType ct; - ct.type_a = p_type; - ct.type_b = p_with_type; - + ConnType ct(p_type, p_with_type); valid_connection_types.insert(ct); } void GraphEdit::remove_valid_connection_type(int p_type, int p_with_type) { - ConnType ct; - ct.type_a = p_type; - ct.type_b = p_with_type; - + ConnType ct(p_type, p_with_type); valid_connection_types.erase(ct); } bool GraphEdit::is_valid_connection_type(int p_type, int p_with_type) const { - ConnType ct; - ct.type_a = p_type; - ct.type_b = p_with_type; - + ConnType ct(p_type, p_with_type); return valid_connection_types.has(ct); } @@ -1646,6 +1637,7 @@ float GraphEdit::get_minimap_opacity() const { void GraphEdit::set_minimap_enabled(bool p_enable) { minimap_button->set_pressed(p_enable); + _minimap_toggled(); minimap->update(); } @@ -1692,10 +1684,10 @@ 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()) { + for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) { if (!r_v.has(E->get())) { return 0; } @@ -1706,7 +1698,7 @@ 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()) { + for (RBSet<StringName>::Element *E = r_u.front(); E; E = E->next()) { if (!r_v.has(E->get())) { return 0; } @@ -1714,7 +1706,7 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u, 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()); } @@ -1722,7 +1714,7 @@ 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()) { + for (RBSet<StringName>::Element *E = r_v.front(); E; E = E->next()) { if (!r_u.has(E->get())) { r_u.insert(E->get()); } @@ -1735,27 +1727,27 @@ 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 RBSet<StringName>::Element *E = p.front(); E; E = E->next()) { + RBSet<StringName> n = r_upper_neighbours[E->get()]; if (_set_operations(GraphEdit::IS_SUBSET, n, z)) { Vector<StringName> t; t.push_back(E->get()); if (!l.has(current_layer)) { - l.set(current_layer, Vector<StringName>{}); + l.insert(current_layer, Vector<StringName>{}); } selected = true; t.append_array(l[current_layer]); - l.set(current_layer, t); - Set<StringName> V; + l.insert(current_layer, t); + RBSet<StringName> V; V.insert(E->get()); _set_operations(GraphEdit::UNION, u, V); } @@ -1797,8 +1789,8 @@ 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()) { +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 RBSet<StringName>::Element *E = r_selected_nodes.front(); E; E = E->next()) { r_root[E->get()] = E->get(); r_align[E->get()] = E->get(); } @@ -1837,7 +1829,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; } @@ -1868,15 +1860,15 @@ void GraphEdit::_crossing_minimisation(HashMap<int, Vector<StringName>> &r_layer } d[q] = crossings; } - c.set(p, d); + c.insert(p, d); } - r_layers.set(i, _split(lower_layer, c)); + r_layers.insert(i, _split(lower_layer, c)); } } -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 RBSet<StringName>::Element *E = r_block_heads.front(); E; E = E->next()) { real_t left = 0; StringName u = E->get(); StringName v = r_align[u]; @@ -2034,7 +2026,7 @@ void GraphEdit::_place_block(StringName p_v, float p_delta, const HashMap<int, V threshold = _calculate_threshold(p_v, w, r_node_name, r_layers, r_root, r_align, r_inner_shift, threshold, r_node_positions); w = r_align[w]; } while (w != p_v); - r_node_positions.set(p_v, pos); + r_node_positions.insert(p_v, pos); } #undef PRED @@ -2048,7 +2040,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)); @@ -2059,7 +2051,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); @@ -2074,7 +2066,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()) { @@ -2090,10 +2082,10 @@ void GraphEdit::arrange_nodes() { ports = p_ports; } } - port_info.set(_connection, ports); + port_info.insert(_connection, ports); } } - upper_neighbours.set(gn->get_name(), s); + upper_neighbours.insert(gn->get_name(), s); } } @@ -2111,13 +2103,13 @@ 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()) { + for (const RBSet<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.set(E->get(), default_position); + new_positions.insert(E->get(), default_position); if ((StringName)root[E->get()] == E->get()) { block_heads.insert(E->get()); } @@ -2125,19 +2117,19 @@ void GraphEdit::arrange_nodes() { _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()) { + for (const RBSet<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); } 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()) { + for (const RBSet<StringName>::Element *E = block_heads.front(); E; E = E->next()) { StringName u = E->get(); float start_from = origin.y + new_positions[E->get()].y; do { Vector2 cal_pos; cal_pos.y = start_from + (real_t)inner_shift[u]; - new_positions.set(u, cal_pos); + new_positions.insert(u, cal_pos); u = align[u]; } while (u != E->get()); } @@ -2169,7 +2161,7 @@ void GraphEdit::arrange_nodes() { } cal_pos.x = current_node_start_pos; } - new_positions.set(layer[j], cal_pos); + new_positions.insert(layer[j], cal_pos); } start_from += largest_node_size + gap_h; @@ -2177,7 +2169,7 @@ void GraphEdit::arrange_nodes() { } emit_signal(SNAME("begin_node_move")); - for (const Set<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) { + for (const RBSet<StringName>::Element *E = selected_nodes.front(); E; E = E->next()) { GraphNode *gn = Object::cast_to<GraphNode>(node_names[E->get()]); gn->set_drag(true); Vector2 pos = (new_positions[E->get()]); @@ -2202,7 +2194,7 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections); ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end); ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs); - ClassDB::bind_method(D_METHOD("set_scroll_ofs", "ofs"), &GraphEdit::set_scroll_ofs); + ClassDB::bind_method(D_METHOD("set_scroll_ofs", "offset"), &GraphEdit::set_scroll_ofs); ClassDB::bind_method(D_METHOD("add_valid_right_disconnect_type", "type"), &GraphEdit::add_valid_right_disconnect_type); ClassDB::bind_method(D_METHOD("remove_valid_right_disconnect_type", "type"), &GraphEdit::remove_valid_right_disconnect_type); @@ -2301,7 +2293,7 @@ void GraphEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("delete_nodes_request")); ADD_SIGNAL(MethodInfo("begin_node_move")); ADD_SIGNAL(MethodInfo("end_node_move")); - ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs"))); + ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset"))); ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::STRING, "slot"), PropertyInfo(Variant::BOOL, "is_output"))); ADD_SIGNAL(MethodInfo("connection_drag_ended")); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index b0d1944d6e..9e34d5528f 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -46,7 +46,7 @@ class GraphEditFilter : public Control { friend class GraphEdit; friend class GraphEditMinimap; - GraphEdit *ge; + GraphEdit *ge = nullptr; virtual bool has_point(const Point2 &p_point) const override; public: @@ -58,7 +58,7 @@ class GraphEditMinimap : public Control { friend class GraphEdit; friend class GraphEditFilter; - GraphEdit *ge; + GraphEdit *ge = nullptr; protected: public: @@ -109,23 +109,23 @@ public: }; private: - Label *zoom_label; - Button *zoom_minus; - Button *zoom_reset; - Button *zoom_plus; + Label *zoom_label = nullptr; + Button *zoom_minus = nullptr; + Button *zoom_reset = nullptr; + Button *zoom_plus = nullptr; - Button *snap_button; - SpinBox *snap_amount; + Button *snap_button = nullptr; + SpinBox *snap_amount = nullptr; - Button *minimap_button; + Button *minimap_button = nullptr; - Button *layout_button; + Button *layout_button = nullptr; - HScrollBar *h_scroll; - VScrollBar *v_scroll; + HScrollBar *h_scroll = nullptr; + VScrollBar *v_scroll = nullptr; float port_grab_distance_horizontal = 0.0; - float port_grab_distance_vertical; + float port_grab_distance_vertical = 0.0; Ref<ViewPanner> panner; bool warped_panning = true; @@ -142,7 +142,7 @@ private: bool connecting_target = false; Vector2 connecting_to; String connecting_target_to; - int connecting_target_index; + int connecting_target_index = 0; bool just_disconnected = false; bool connecting_valid = false; Vector2 click_pos; @@ -155,8 +155,9 @@ private: float zoom = 1.0; float zoom_step = 1.2; - float zoom_min; - float zoom_max; + // Proper values set in constructor. + float zoom_min = 0.0; + float zoom_max = 0.0; void _zoom_minus(); void _zoom_reset(); @@ -190,9 +191,9 @@ private: void _scroll_moved(double); virtual void gui_input(const Ref<InputEvent> &p_ev) override; - Control *connections_layer; - GraphEditFilter *top_layer; - GraphEditMinimap *minimap; + Control *connections_layer = nullptr; + GraphEditFilter *top_layer = nullptr; + GraphEditMinimap *minimap = nullptr; void _top_layer_input(const Ref<InputEvent> &p_ev); bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); @@ -206,7 +207,7 @@ private: Array _get_connection_list() const; - bool lines_on_bg; + bool lines_on_bg = false; struct ConnType { union { @@ -227,16 +228,16 @@ 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); void _set_drag_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, bool p_drag); void _set_position_of_comment_enclosed_nodes(GraphNode *p_node, HashMap<StringName, Vector<GraphNode *>> &p_comment_enclosed_nodes, Vector2 p_pos); - HBoxContainer *zoom_hb; + HBoxContainer *zoom_hb = nullptr; friend class GraphEditFilter; bool _filter_input(const Point2 &p_point); @@ -257,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 ef0ac75cb4..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 ? "commentfocus" : "comment"); + sb = get_theme_stylebox(selected ? SNAME("comment_focus") : SNAME("comment")); } else { - sb = get_theme_stylebox(selected ? "selectedframe" : "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,14 +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->set_width(w); 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(); @@ -412,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()) { @@ -420,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()) { @@ -427,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) { @@ -483,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) && @@ -502,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; @@ -623,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++) { @@ -645,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); @@ -990,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); @@ -1012,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 0c07d82663..ec33018da0 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -33,13 +33,13 @@ 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("hseparation")); - int vsep = get_theme_constant(SNAME("vseparation")); + int hsep = get_theme_constant(SNAME("h_separation")); + int vsep = get_theme_constant(SNAME("v_separation")); int max_col = MIN(get_child_count(), columns); int max_row = ceil((float)get_child_count() / (float)columns); @@ -50,6 +50,9 @@ void GridContainer::_notification(int p_what) { if (!c || !c->is_visible_in_tree()) { continue; } + if (c->is_set_as_top_level()) { + continue; + } int row = valid_controls_index / columns; int col = valid_controls_index % columns; @@ -101,7 +104,7 @@ 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()) { + for (RBSet<int>::Element *E = col_expanded.front(); E; E = E->next()) { if (col_minw[E->get()] > col_minw[max_index]) { max_index = E->get(); } @@ -122,7 +125,7 @@ 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()) { + for (RBSet<int>::Element *E = row_expanded.front(); E; E = E->next()) { if (row_minh[E->get()] > row_minh[max_index]) { max_index = E->get(); } @@ -258,11 +261,11 @@ 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("hseparation")); - int vsep = get_theme_constant(SNAME("vseparation")); + int hsep = get_theme_constant(SNAME("h_separation")); + int vsep = get_theme_constant(SNAME("v_separation")); int max_row = 0; int max_col = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 8c0f696a9f..8b22f3722a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -585,6 +585,9 @@ Size2 ItemList::Item::get_icon_size() const { void ItemList::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); +#define CAN_SELECT(i) (items[i].selectable && !items[i].disabled) +#define IS_SAME_ROW(i, row) (i / current_columns == row) + double prev_scroll = scroll_bar->get_value(); Ref<InputEventMouseMotion> mm = p_event; @@ -603,7 +606,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { return; } - if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT)) && mb->is_pressed()) { + if (mb.is_valid() && mb->is_pressed()) { search_string = ""; //any mousepress cancels Vector2 pos = mb->get_position(); Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); @@ -628,7 +631,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } } - if (closest != -1) { + if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) { int i = closest; if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) { @@ -642,55 +645,47 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { SWAP(from, to); } for (int j = from; j <= to; j++) { + if (!CAN_SELECT(j)) { + continue; + } bool selected = !items[j].selected; select(j, false); if (selected) { emit_signal(SNAME("multi_selected"), j, true); } } + emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); - if (mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } } else { if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) { defer_select_single = i; return; } - if (items[i].selected && mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } else { - bool selected = items[i].selected; - + if (!items[i].selected || allow_reselect) { select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed()); - if (!selected || allow_reselect) { - if (select_mode == SELECT_SINGLE) { - emit_signal(SNAME("item_selected"), i); - } else { - emit_signal(SNAME("multi_selected"), i, true); - } + if (select_mode == SELECT_SINGLE) { + emit_signal(SNAME("item_selected"), i); + } else { + emit_signal(SNAME("multi_selected"), i, true); } + } - if (mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position()); - } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) { - emit_signal(SNAME("item_activated"), i); - } + emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index()); + + if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { + emit_signal(SNAME("item_activated"), i); } } return; + } else if (closest != -1) { + emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index()); + } else { + // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. + emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index()); } - if (mb->get_button_index() == MouseButton::RIGHT) { - emit_signal(SNAME("rmb_clicked"), mb->get_position()); - - return; - } - - // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting. - emit_signal(SNAME("nothing_selected")); } if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) { scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8); @@ -707,7 +702,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) { for (int i = current - 1; i >= 0; i--) { - if (items[i].text.begins_with(search_string)) { + if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) { set_current(i); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -723,7 +718,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } if (current >= current_columns) { - set_current(current - current_columns); + int next = current - current_columns; + while (next >= 0 && !CAN_SELECT(next)) { + next = next - current_columns; + } + if (next < 0) { + accept_event(); + return; + } + set_current(next); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { emit_signal(SNAME("item_selected"), current); @@ -737,7 +740,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) { for (int i = current + 1; i < items.size(); i++) { - if (items[i].text.begins_with(search_string)) { + if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) { set_current(i); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -752,7 +755,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { } if (current < items.size() - current_columns) { - set_current(current + current_columns); + int next = current + current_columns; + while (next < items.size() && !CAN_SELECT(next)) { + next = next + current_columns; + } + if (next >= items.size()) { + accept_event(); + return; + } + set_current(next); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { emit_signal(SNAME("item_selected"), current); @@ -763,7 +774,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { search_string = ""; //any mousepress cancels for (int i = 4; i > 0; i--) { - if (current - current_columns * i >= 0) { + if (current - current_columns * i >= 0 && CAN_SELECT(current - current_columns * i)) { set_current(current - current_columns * i); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -777,7 +788,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { search_string = ""; //any mousepress cancels for (int i = 4; i > 0; i--) { - if (current + current_columns * i < items.size()) { + if (current + current_columns * i < items.size() && CAN_SELECT(current + current_columns * i)) { set_current(current + current_columns * i); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { @@ -792,7 +803,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { search_string = ""; //any mousepress cancels if (current % current_columns != 0) { - set_current(current - 1); + int current_row = current / current_columns; + int next = current - 1; + while (!CAN_SELECT(next)) { + next = next - 1; + } + if (next < 0 || !IS_SAME_ROW(next, current_row)) { + accept_event(); + return; + } + set_current(next); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { emit_signal(SNAME("item_selected"), current); @@ -803,7 +823,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { search_string = ""; //any mousepress cancels if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) { - set_current(current + 1); + int current_row = current / current_columns; + int next = current + 1; + while (!CAN_SELECT(next)) { + next = next + 1; + } + if (items.size() <= next || !IS_SAME_ROW(next, current_row)) { + accept_event(); + return; + } + set_current(next); ensure_current_is_visible(); if (select_mode == SELECT_SINGLE) { emit_signal(SNAME("item_selected"), current); @@ -879,6 +908,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { if (scroll_bar->get_value() != prev_scroll) { accept_event(); //accept event if scroll changed } + +#undef CAN_SELECT +#undef IS_SAME_ROW } void ItemList::ensure_current_is_visible() { @@ -937,8 +969,8 @@ void ItemList::_notification(int p_what) { draw_style_box(bg, Rect2(Point2(), size)); - int hseparation = get_theme_constant(SNAME("hseparation")); - int vseparation = get_theme_constant(SNAME("vseparation")); + int hseparation = get_theme_constant(SNAME("h_separation")); + int vseparation = get_theme_constant(SNAME("v_separation")); int icon_margin = get_theme_constant(SNAME("icon_margin")); int line_separation = get_theme_constant(SNAME("line_separation")); Color font_outline_color = get_theme_color(SNAME("font_outline_color")); @@ -1744,11 +1776,10 @@ void ItemList::_bind_methods() { BIND_ENUM_CONSTANT(SELECT_MULTI); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index"))); - ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"))); + ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); + ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index"))); - ADD_SIGNAL(MethodInfo("rmb_clicked", PropertyInfo(Variant::VECTOR2, "at_position"))); - ADD_SIGNAL(MethodInfo("nothing_selected")); GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 96735678c1..ffbe7d055a 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -98,7 +98,7 @@ private: SelectMode select_mode = SELECT_SINGLE; IconMode icon_mode = ICON_MODE_LEFT; - VScrollBar *scroll_bar; + VScrollBar *scroll_bar = nullptr; TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_TRIM_ELLIPSIS; uint64_t search_time_msec = 0; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index cd6fc168c2..eda3d40f63 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -648,21 +648,21 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) { } } -void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void Label::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; - font_dirty = true; + dirty = true; update(); } } -Control::StructuredTextParser Label::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser Label::get_structured_text_bidi_override() const { return st_parser; } void Label::set_structured_text_bidi_override_options(Array p_args) { st_args = p_args; - font_dirty = true; + dirty = true; update(); } diff --git a/scene/gui/label.h b/scene/gui/label.h index 0b931b3084..f7b725928f 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -80,7 +80,7 @@ private: Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; float percent_visible = 1.0; @@ -124,8 +124,8 @@ public: void set_language(const String &p_language); String get_language() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() 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; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e063d3aeba..73188d6602 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -215,6 +215,27 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } +void LineEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } + // Handle Unicode (with modifiers active, process after shortcuts). + if (has_focus() && editable && (k->get_unicode() >= 32)) { + selection_delete(); + char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 }; + int prev_len = text.length(); + insert_text_at_caret(ucodestr); + if (text.length() != prev_len) { + _text_changed(); + } + accept_event(); + } + } +} + void LineEdit::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -385,9 +406,45 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (k.is_valid()) { if (!k->is_pressed()) { + if (alt_start && k->get_keycode() == Key::ALT) { + alt_start = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + char32_t ucodestr[2] = { (char32_t)alt_code, 0 }; + insert_text_at_caret(ucodestr); + } + accept_event(); + return; + } return; } + // Alt+ Unicode input: + if (k->is_alt_pressed()) { + if (!alt_start) { + if (k->get_keycode() == Key::KP_ADD) { + alt_start = true; + alt_code = 0; + accept_event(); + return; + } + } else { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); + } + if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); + } + if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; + } + accept_event(); + return; + } + } + if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { _ensure_menu(); @@ -1431,7 +1488,7 @@ bool LineEdit::get_draw_control_chars() const { return draw_control_chars; } -void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void LineEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; _shape(); @@ -1439,7 +1496,7 @@ void LineEdit::set_structured_text_bidi_override(Control::StructuredTextParser p } } -Control::StructuredTextParser LineEdit::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser LineEdit::get_structured_text_bidi_override() const { return st_parser; } @@ -2445,6 +2502,7 @@ LineEdit::LineEdit(const String &p_placeholder) { set_focus_mode(FOCUS_ALL); set_default_cursor_shape(CURSOR_IBEAM); set_mouse_filter(MOUSE_FILTER_STOP); + set_process_unhandled_key_input(true); caret_blink_timer = memnew(Timer); add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 444c9a1c50..0fb178fca4 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -77,6 +77,9 @@ private: bool pass = false; bool text_changed_dirty = false; + bool alt_start = false; + uint32_t alt_code = 0; + String undo_text; String text; String placeholder; @@ -106,7 +109,7 @@ private: String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; TextDirection input_direction = TEXT_DIRECTION_LTR; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; bool draw_control_chars = false; @@ -202,6 +205,7 @@ private: protected: void _notification(int p_what); static void _bind_methods(); + virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; virtual void gui_input(const Ref<InputEvent> &p_event) override; bool _set(const StringName &p_name, const Variant &p_value); @@ -252,8 +256,8 @@ public: void set_draw_control_chars(bool p_draw_control_chars); bool get_draw_control_chars() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() 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; diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index dc4f09d22d..dca6437519 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -61,7 +61,7 @@ String LinkButton::get_text() const { return text; } -void LinkButton::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void LinkButton::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; _shape(); @@ -69,7 +69,7 @@ void LinkButton::set_structured_text_bidi_override(Control::StructuredTextParser } } -Control::StructuredTextParser LinkButton::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser LinkButton::get_structured_text_bidi_override() const { return st_parser; } diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index f996558f32..6d2dcbde84 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -53,7 +53,7 @@ private: Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; void _shape(); @@ -71,8 +71,8 @@ public: void set_text(const String &p_text); String get_text() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() 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; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 7e724e4d71..316fee53fe 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -33,7 +33,7 @@ #include "core/os/keyboard.h" #include "scene/main/window.h" -void MenuButton::unhandled_key_input(const Ref<InputEvent> &p_event) { +void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!_is_focus_owner_in_shortcut_context()) { @@ -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); @@ -232,7 +232,7 @@ MenuButton::MenuButton(const String &p_text) : set_flat(true); set_toggle_mode(true); set_disable_shortcuts(false); - set_process_unhandled_key_input(true); + set_process_shortcut_input(true); set_focus_mode(FOCUS_NONE); set_action_mode(ACTION_MODE_BUTTON_PRESS); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 9cfb780255..0a6b46c796 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -40,7 +40,7 @@ class MenuButton : public Button { bool clicked = false; bool switch_on_hover = false; bool disable_shortcuts = false; - PopupMenu *popup; + PopupMenu *popup = nullptr; Vector2i mouse_pos_adjusted; @@ -54,7 +54,7 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); - virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; + virtual void shortcut_input(const Ref<InputEvent> &p_event) override; public: virtual void pressed() override; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 1e8a149e11..4b79d79846 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -42,7 +42,7 @@ Size2 OptionButton::get_minimum_size() const { const Size2 arrow_size = Control::get_theme_icon(SNAME("arrow"))->get_size(); Size2 content_size = minsize - padding; - content_size.width += arrow_size.width + get_theme_constant(SNAME("hseparation")); + content_size.width += arrow_size.width + get_theme_constant(SNAME("h_separation")); content_size.height = MAX(content_size.height, arrow_size.height); minsize = content_size + padding; @@ -203,16 +203,18 @@ void OptionButton::pressed() { } void OptionButton::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id) { + bool first_selectable = !has_selectable_items(); popup->add_icon_radio_check_item(p_icon, p_label, p_id); - if (popup->get_item_count() == 1) { - select(0); + if (first_selectable) { + select(get_item_count() - 1); } } void OptionButton::add_item(const String &p_label, int p_id) { + bool first_selectable = !has_selectable_items(); popup->add_radio_check_item(p_label, p_id); - if (popup->get_item_count() == 1) { - select(0); + if (first_selectable) { + select(get_item_count() - 1); } } @@ -280,6 +282,9 @@ bool OptionButton::is_item_disabled(int p_idx) const { return popup->is_item_disabled(p_idx); } +bool OptionButton::is_item_separator(int p_idx) const { + return popup->is_item_separator(p_idx); +} void OptionButton::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); @@ -299,12 +304,37 @@ void OptionButton::set_item_count(int p_count) { notify_property_list_changed(); } +bool OptionButton::has_selectable_items() const { + for (int i = 0; i < get_item_count(); i++) { + if (!is_item_disabled(i) && !is_item_separator(i)) { + return true; + } + } + return false; +} +int OptionButton::get_selectable_item(bool p_from_last) const { + if (!p_from_last) { + for (int i = 0; i < get_item_count(); i++) { + if (!is_item_disabled(i) && !is_item_separator(i)) { + return i; + } + } + } else { + for (int i = get_item_count() - 1; i >= 0; i++) { + if (!is_item_disabled(i) && !is_item_separator(i)) { + return i; + } + } + } + return -1; +} + int OptionButton::get_item_count() const { return popup->get_item_count(); } -void OptionButton::add_separator() { - popup->add_separator(); +void OptionButton::add_separator(const String &p_text) { + popup->add_separator(p_text); } void OptionButton::clear() { @@ -407,7 +437,8 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &OptionButton::get_item_metadata); ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &OptionButton::get_item_tooltip); ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &OptionButton::is_item_disabled); - ClassDB::bind_method(D_METHOD("add_separator"), &OptionButton::add_separator); + ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &OptionButton::is_item_separator); + ClassDB::bind_method(D_METHOD("add_separator", "text"), &OptionButton::add_separator, DEFVAL(String())); ClassDB::bind_method(D_METHOD("clear"), &OptionButton::clear); ClassDB::bind_method(D_METHOD("select", "idx"), &OptionButton::select); ClassDB::bind_method(D_METHOD("get_selected"), &OptionButton::get_selected); @@ -420,6 +451,8 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_count", "count"), &OptionButton::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count); + ClassDB::bind_method(D_METHOD("has_selectable_items"), &OptionButton::has_selectable_items); + ClassDB::bind_method(D_METHOD("get_selectable_item", "from_last"), &OptionButton::get_selectable_item, DEFVAL(false)); // "selected" property must come after "item_count", otherwise GH-10213 occurs. ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_"); diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 732730e0f4..7896132626 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -37,7 +37,7 @@ class OptionButton : public Button { GDCLASS(OptionButton, Button); - PopupMenu *popup; + PopupMenu *popup = nullptr; int current = -1; void _focused(int p_which); @@ -77,12 +77,16 @@ public: int get_item_index(int p_id) const; Variant get_item_metadata(int p_idx) const; bool is_item_disabled(int p_idx) const; + bool is_item_separator(int p_idx) const; String get_item_tooltip(int p_idx) const; + bool has_selectable_items() const; + int get_selectable_item(bool p_from_last = false) const; + void set_item_count(int p_count); int get_item_count() const; - void add_separator(); + void add_separator(const String &p_text = ""); void clear(); diff --git a/scene/gui/popup.h b/scene/gui/popup.h index c45f4ddc24..6211af4d20 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -65,7 +65,7 @@ public: class PopupPanel : public Popup { GDCLASS(PopupPanel, Popup); - Panel *panel; + Panel *panel = nullptr; protected: void _update_child_rects(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 9fc1fb072c..8303d6db57 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -47,8 +47,8 @@ String PopupMenu::_get_accel_text(const Item &p_item) const { } Size2 PopupMenu::_get_contents_minimum_size() const { - int vseparation = get_theme_constant(SNAME("vseparation")); - int hseparation = get_theme_constant(SNAME("hseparation")); + int vseparation = get_theme_constant(SNAME("v_separation")); + int hseparation = get_theme_constant(SNAME("h_separation")); Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content @@ -129,7 +129,7 @@ int PopupMenu::_get_item_height(int p_item) const { } int PopupMenu::_get_items_total_height() const { - int vsep = get_theme_constant(SNAME("vseparation")); + int vsep = get_theme_constant(SNAME("v_separation")); // Get total height of all items by taking max of icon height and font height int items_total_height = 0; @@ -148,7 +148,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); // Accounts for margin in the margin container - int vseparation = get_theme_constant(SNAME("vseparation")); + int vseparation = get_theme_constant(SNAME("v_separation")); Point2 ofs = style->get_offset() + Point2(0, vseparation / 2); @@ -169,7 +169,7 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const { return -1; } -void PopupMenu::_activate_submenu(int p_over) { +void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { Node *n = get_node(items[p_over].submenu); ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[p_over].submenu + "."); Popup *submenu_popup = Object::cast_to<Popup>(n); @@ -179,7 +179,7 @@ void PopupMenu::_activate_submenu(int p_over) { } Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); - int vsep = get_theme_constant(SNAME("vseparation")); + int vsep = get_theme_constant(SNAME("v_separation")); Point2 this_pos = get_position(); Rect2 this_rect(this_pos, get_size()); @@ -213,8 +213,10 @@ void PopupMenu::_activate_submenu(int p_over) { return; } + submenu_pum->activated_by_keyboard = p_by_keyboard; + // If not triggered by the mouse, start the popup with its first item selected. - if (submenu_pum->get_item_count() > 0 && Input::get_singleton()->is_action_just_pressed("ui_accept")) { + if (submenu_pum->get_item_count() > 0 && p_by_keyboard) { submenu_pum->set_current_index(0); } @@ -323,14 +325,14 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { set_input_as_handled(); } } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !!items[mouse_over].separator && items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over); + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + _activate_submenu(mouse_over, true); set_input_as_handled(); } } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over); + _activate_submenu(mouse_over, true); } else { activate_item(mouse_over); } @@ -396,6 +398,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { + if (m->get_velocity().is_equal_approx(Vector2())) { + return; + } + activated_by_keyboard = false; + for (const Rect2 &E : autohide_areas) { if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) { _close_pressed(); @@ -497,8 +504,8 @@ void PopupMenu::_draw_items() { Ref<StyleBox> labeled_separator_left = get_theme_stylebox(SNAME("labeled_separator_left")); Ref<StyleBox> labeled_separator_right = get_theme_stylebox(SNAME("labeled_separator_right")); - int vseparation = get_theme_constant(SNAME("vseparation")); - int hseparation = get_theme_constant(SNAME("hseparation")); + int vseparation = get_theme_constant(SNAME("v_separation")); + int hseparation = get_theme_constant(SNAME("h_separation")); Color font_color = get_theme_color(SNAME("font_color")); Color font_disabled_color = get_theme_color(SNAME("font_disabled_color")); Color font_accelerator_color = get_theme_color(SNAME("font_accelerator_color")); @@ -557,10 +564,8 @@ void PopupMenu::_draw_items() { // Separator item_ofs.x += items[i].h_ofs; if (items[i].separator) { - int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; - int sep_ofs = Math::floor((h - sep_h) / 2.0); if (!text.is_empty() || !items[i].icon.is_null()) { - int content_size = items[i].text_buf->get_size().width; + int content_size = items[i].text_buf->get_size().width + hseparation * 2; if (!items[i].icon.is_null()) { content_size += icon_size.width + hseparation; } @@ -569,12 +574,18 @@ void PopupMenu::_draw_items() { int content_left = content_center - content_size / 2; int content_right = content_center + content_size / 2; if (content_left > item_ofs.x) { + int sep_h = labeled_separator_left->get_center_size().height + labeled_separator_left->get_minimum_size().height; + int sep_ofs = Math::floor((h - sep_h) / 2.0); labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); } if (content_right < display_width) { + int sep_h = labeled_separator_right->get_center_size().height + labeled_separator_right->get_minimum_size().height; + int sep_ofs = Math::floor((h - sep_h) / 2.0); labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); } } else { + int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; + int sep_ofs = Math::floor((h - sep_h) / 2.0); separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } } @@ -624,23 +635,28 @@ void PopupMenu::_draw_items() { } } - // Text Color font_outline_color = get_theme_color(SNAME("font_outline_color")); int outline_size = get_theme_constant(SNAME("outline_size")); + + // Text if (items[i].separator) { + Color font_separator_outline_color = get_theme_color(SNAME("font_separator_outline_color")); + int separator_outline_size = get_theme_constant(SNAME("separator_outline_size")); + if (!text.is_empty()) { Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); if (!rtl && !items[i].icon.is_null()) { text_pos.x += icon_size.width + hseparation; } - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + if (separator_outline_size > 0 && font_separator_outline_color.a > 0) { + items[i].text_buf->draw_outline(ci, text_pos, separator_outline_size, font_separator_outline_color); } items[i].text_buf->draw(ci, text_pos, font_separator_color); } } else { item_ofs.x += icon_ofs + check_ofs; + if (rtl) { Vector2 text_pos = Size2(control->get_size().width - items[i].text_buf->get_size().width - item_ofs.x, item_ofs.y) + Point2(0, Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); if (outline_size > 0 && font_outline_color.a > 0) { @@ -687,7 +703,7 @@ void PopupMenu::_draw_background() { void PopupMenu::_minimum_lifetime_timeout() { close_allowed = true; // If the mouse still isn't in this popup after timer expires, close. - if (!get_visible_rect().has_point(get_mouse_position())) { + if (!activated_by_keyboard && !get_visible_rect().has_point(get_mouse_position())) { _close_pressed(); } } @@ -713,8 +729,8 @@ void PopupMenu::_shape_item(int p_item) { if (items.write[p_item].dirty) { items.write[p_item].text_buf->clear(); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + Ref<Font> font = get_theme_font(items[p_item].separator ? SNAME("font_separator") : SNAME("font")); + int font_size = get_theme_font_size(items[p_item].separator ? SNAME("font_separator_size") : SNAME("font_size")); if (items[p_item].text_direction == Control::TEXT_DIRECTION_INHERITED) { items.write[p_item].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -772,7 +788,7 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { // Only used when using operating system windows. - if (!is_embedded() && autohide_areas.size()) { + if (!activated_by_keyboard && !is_embedded() && autohide_areas.size()) { Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position(); mouse_pos -= get_position(); @@ -818,10 +834,10 @@ void PopupMenu::_notification(int p_what) { // Set margin on the margin container Ref<StyleBox> panel_style = get_theme_stylebox(SNAME("panel")); - margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP)); - margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM)); margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Side::SIDE_LEFT)); + margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Side::SIDE_TOP)); margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Side::SIDE_RIGHT)); + margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Side::SIDE_BOTTOM)); } } break; } diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 5ce55209d4..12587b7e73 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -87,9 +87,10 @@ class PopupMenu : public Popup { }; bool close_allowed = false; + bool activated_by_keyboard = false; Timer *minimum_lifetime_timer = nullptr; - Timer *submenu_timer; + Timer *submenu_timer = nullptr; List<Rect2> autohide_areas; Vector<Item> items; MouseButton initial_button_mask = MouseButton::NONE; @@ -107,7 +108,7 @@ class PopupMenu : public Popup { void _shape_item(int p_item); virtual void gui_input(const Ref<InputEvent> &p_event); - void _activate_submenu(int p_over); + void _activate_submenu(int p_over, bool p_by_keyboard = false); void _submenu_timeout(); uint64_t popup_time_msec = 0; @@ -116,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); @@ -125,9 +126,9 @@ class PopupMenu : public Popup { uint64_t search_time_msec = 0; String search_string = ""; - MarginContainer *margin_container; - ScrollContainer *scroll_container; - Control *control; + MarginContainer *margin_container = nullptr; + ScrollContainer *scroll_container = nullptr; + Control *control = nullptr; void _draw_items(); void _draw_background(); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 50ffb3ca67..f36682942f 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -62,15 +62,42 @@ void ProgressBar::_notification(int p_what) { Color font_color = get_theme_color(SNAME("font_color")); draw_style_box(bg, Rect2(Point2(), get_size())); + float r = get_as_ratio(); - int mp = fg->get_minimum_size().width; - int p = r * (get_size().width - mp); - if (p > 0) { - if (is_layout_rtl()) { - draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height))); - } else { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); - } + + switch (mode) { + case FILL_BEGIN_TO_END: + case FILL_END_TO_BEGIN: { + int mp = fg->get_minimum_size().width; + int p = round(r * (get_size().width - mp)); + // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL, + // and left to right otherwise. And likewise for FILL_END_TO_BEGIN. + bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN); + if (p > 0) { + if (right_to_left) { + int p_remaining = round((1.0 - r) * (get_size().width - mp)); + draw_style_box(fg, Rect2(Point2(p_remaining, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } else { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } + } + } break; + case FILL_TOP_TO_BOTTOM: + case FILL_BOTTOM_TO_TOP: { + int mp = fg->get_minimum_size().height; + int p = round(r * (get_size().height - mp)); + + if (p > 0) { + if (mode == FILL_TOP_TO_BOTTOM) { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(get_size().width, p + fg->get_minimum_size().height))); + } else { + int p_remaining = round((1.0 - r) * (get_size().height - mp)); + draw_style_box(fg, Rect2(Point2(0, p_remaining), Size2(get_size().width, p + fg->get_minimum_size().height))); + } + } + } break; + case FILL_MODE_MAX: + break; } if (percent_visible) { @@ -88,6 +115,16 @@ void ProgressBar::_notification(int p_what) { } } +void ProgressBar::set_fill_mode(int p_fill) { + ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); + mode = (FillMode)p_fill; + update(); +} + +int ProgressBar::get_fill_mode() { + return mode; +} + void ProgressBar::set_percent_visible(bool p_visible) { percent_visible = p_visible; update(); @@ -98,10 +135,18 @@ bool ProgressBar::is_percent_visible() const { } void ProgressBar::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode); + ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode); ClassDB::bind_method(D_METHOD("set_percent_visible", "visible"), &ProgressBar::set_percent_visible); ClassDB::bind_method(D_METHOD("is_percent_visible"), &ProgressBar::is_percent_visible); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "percent_visible"), "set_percent_visible", "is_percent_visible"); + + BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END); + BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN); + BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM); + BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP); } ProgressBar::ProgressBar() { diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index 2d89163f78..5ba21ad7d5 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -43,11 +43,27 @@ protected: static void _bind_methods(); public: + enum FillMode { + FILL_BEGIN_TO_END, + FILL_END_TO_BEGIN, + FILL_TOP_TO_BOTTOM, + FILL_BOTTOM_TO_TOP, + FILL_MODE_MAX + }; + + void set_fill_mode(int p_fill); + int get_fill_mode(); + void set_percent_visible(bool p_visible); bool is_percent_visible() const; Size2 get_minimum_size() const override; ProgressBar(); + +private: + FillMode mode = FILL_BEGIN_TO_END; }; +VARIANT_ENUM_CAST(ProgressBar::FillMode); + #endif // PROGRESS_BAR_H diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 8e66826e9d..73f19a8eda 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -50,7 +50,7 @@ void Range::_value_changed_notify() { } void Range::Shared::emit_value_changed() { - for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) { + for (RBSet<Range *>::Element *E = owners.front(); E; E = E->next()) { Range *r = E->get(); if (!r->is_inside_tree()) { continue; @@ -70,7 +70,7 @@ void Range::_validate_values() { } void Range::Shared::emit_changed(const char *p_what) { - for (Set<Range *>::Element *E = owners.front(); E; E = E->next()) { + for (RBSet<Range *>::Element *E = owners.front(); E; E = E->next()) { Range *r = E->get(); if (!r->is_inside_tree()) { continue; diff --git a/scene/gui/range.h b/scene/gui/range.h index 597c50ca26..a59bfa9677 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -45,12 +45,12 @@ 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 = ""); }; - Shared *shared; + Shared *shared = nullptr; void _ref_shared(Shared *p_shared); void _unref_shared(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 70755a2870..fa0c2ce12c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -30,6 +30,7 @@ #include "rich_text_label.h" +#include "core/input/input_map.h" #include "core/math/math_defs.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -142,7 +143,7 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item for (Item *it = p_item_from; it && it != p_item_to; it = _get_next_item(it)) { switch (it->type) { case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); offset += t->text.length(); if (offset > p_position) { return it; @@ -166,16 +167,16 @@ String RichTextLabel::_roman(int p_num, bool p_capitalize) const { }; String s; if (p_capitalize) { - String M[] = { "", "M", "MM", "MMM" }; - String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; - String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; - String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; + const String M[] = { "", "M", "MM", "MMM" }; + const String C[] = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; + const String X[] = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; + const String I[] = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; } else { - String M[] = { "", "m", "mm", "mmm" }; - String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }; - String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }; - String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }; + const String M[] = { "", "m", "mm", "mmm" }; + const String C[] = { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" }; + const String X[] = { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" }; + const String I[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" }; s = M[p_num / 1000] + C[(p_num % 1000) / 100] + X[(p_num % 100) / 10] + I[p_num % 10]; } return s; @@ -215,7 +216,7 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< RID t = l.text_buf->get_rid(); int spans = TS->shaped_get_span_count(t); for (int i = 0; i < spans; i++) { - ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i); + ItemText *it = reinterpret_cast<ItemText *>((uint64_t)TS->shaped_get_span_meta(t, i)); if (it) { Ref<Font> font = _find_font(it); if (font.is_null()) { @@ -269,8 +270,8 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> switch (it->type) { case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - int hseparation = get_theme_constant(SNAME("table_hseparation")); - int vseparation = get_theme_constant(SNAME("table_vseparation")); + int hseparation = get_theme_constant(SNAME("table_h_separation")); + int vseparation = get_theme_constant(SNAME("table_v_separation")); int col_count = table->columns.size(); for (int i = 0; i < col_count; i++) { @@ -466,15 +467,11 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> switch (it->type) { case ITEM_DROPCAP: { // Add dropcap. - const ItemDropcap *dc = (ItemDropcap *)it; - if (dc != nullptr) { - l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins); - l.dc_color = dc->color; - l.dc_ol_size = dc->ol_size; - l.dc_ol_color = dc->ol_color; - } else { - l.text_buf->clear_dropcap(); - } + const ItemDropcap *dc = static_cast<ItemDropcap *>(it); + l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins); + l.dc_color = dc->color; + l.dc_ol_size = dc->ol_size; + l.dc_ol_color = dc->ol_color; } break; case ITEM_NEWLINE: { Ref<Font> font = _find_font(it); @@ -491,7 +488,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> remaining_characters--; } break; case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); Ref<Font> font = _find_font(it); if (font.is_null()) { font = p_base_font; @@ -513,7 +510,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> l.char_count += tx.length(); } break; case ITEM_IMAGE: { - ItemImage *img = (ItemImage *)it; + ItemImage *img = static_cast<ItemImage *>(it); l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1); text += String::chr(0xfffc); l.char_count++; @@ -521,8 +518,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } break; case ITEM_TABLE: { ItemTable *table = static_cast<ItemTable *>(it); - int hseparation = get_theme_constant(SNAME("table_hseparation")); - int vseparation = get_theme_constant(SNAME("table_vseparation")); + int hseparation = get_theme_constant(SNAME("table_h_separation")); + int vseparation = get_theme_constant(SNAME("table_v_separation")); int col_count = table->columns.size(); int t_char_count = 0; // Set minimums to zero. @@ -842,7 +839,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw inlined objects. Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { - Item *it = (Item *)(uint64_t)objects[i]; + Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]); if (it != nullptr) { Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS @@ -856,7 +853,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Color odd_row_bg = get_theme_color(SNAME("table_odd_row_bg")); Color even_row_bg = get_theme_color(SNAME("table_even_row_bg")); Color border = get_theme_color(SNAME("table_border")); - int hseparation = get_theme_constant(SNAME("table_hseparation")); + int hseparation = get_theme_constant(SNAME("table_h_separation")); int col_count = table->columns.size(); int row_count = table->rows.size(); @@ -944,8 +941,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } //Apply fx. - float faded_visibility = 1.0f; if (fade) { + float faded_visibility = 1.0f; if (glyphs[i].start >= fade->starting_index) { faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; @@ -1160,8 +1157,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } //Apply fx. - float faded_visibility = 1.0f; if (fade) { + float faded_visibility = 1.0f; if (glyphs[i].start >= fade->starting_index) { faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length; faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; @@ -1330,14 +1327,22 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item } } -float RichTextLabel::_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, int *r_click_line, Item **r_click_item, int *r_click_char) { +float RichTextLabel::_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, int *r_click_line, Item **r_click_item, int *r_click_char, bool p_table) { Vector2 off; int char_pos = -1; Line &l = p_frame->lines.write[p_line]; bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL); bool lrtl = is_layout_rtl(); + + // Table hit test results. bool table_hit = false; + Vector2i table_range; + float table_offy = 0.f; + ItemFrame *table_click_frame = nullptr; + int table_click_line = -1; + Item *table_click_item = nullptr; + int table_click_char = -1; for (int line = 0; line < l.text_buf->get_line_count(); line++) { RID rid = l.text_buf->get_line_rid(line); @@ -1378,19 +1383,18 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { - Item *it = (Item *)(uint64_t)objects[i]; + Item *it = reinterpret_cast<Item *>((uint64_t)objects[i]); if (it != nullptr) { Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); - if (rect.has_point(p_click - p_ofs - off)) { + rect.position += p_ofs + off; + if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { switch (it->type) { case ITEM_TABLE: { - int hseparation = get_theme_constant(SNAME("table_hseparation")); - int vseparation = get_theme_constant(SNAME("table_vseparation")); + int hseparation = get_theme_constant(SNAME("table_h_separation")); + int vseparation = get_theme_constant(SNAME("table_v_separation")); ItemTable *table = static_cast<ItemTable *>(it); - table_hit = true; - int idx = 0; int col_count = table->columns.size(); int row_count = table->rows.size(); @@ -1406,7 +1410,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (rtl) { coff.x = rect.size.width - table->columns[col].width - coff.x; } - Rect2 crect = Rect2(p_ofs + off + rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); + Rect2 crect = Rect2(rect.position + coff - frame->padding.position, Size2(table->columns[col].width + hseparation, table->rows[row] + vseparation) + frame->padding.position + frame->padding.size); if (col == col_count - 1) { if (rtl) { crect.size.x = crect.position.x + crect.size.x; @@ -1417,9 +1421,19 @@ 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++) { - _find_click_in_line(frame, j, p_ofs + off + rect.position + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_click, r_click_frame, r_click_line, r_click_item, r_click_char); - if (((r_click_item != nullptr) && ((*r_click_item) != nullptr)) || ((r_click_frame != nullptr) && ((*r_click_frame) != nullptr))) { - return off.y; + _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++) { + 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); + } + } + table_offy = off.y; + table_hit = true; } } } @@ -1433,14 +1447,39 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } } } - Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)), Size2(get_size().x, TS->shaped_text_get_size(rid).y)); + Rect2 rect = Rect2(p_ofs + off - Vector2(0, TS->shaped_text_get_ascent(rid)) - p_frame->padding.position, TS->shaped_text_get_size(rid) + p_frame->padding.position + p_frame->padding.size); + if (p_table) { + rect.size.y += get_theme_constant(SNAME("table_v_separation")); + } - if (rect.has_point(p_click) && !table_hit) { + if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) { char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x); } + + // If table hit was detected, and line hit is in the table bounds use table hit. + if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) { + if (r_click_frame != nullptr) { + *r_click_frame = table_click_frame; + } + + if (r_click_line != nullptr) { + *r_click_line = table_click_line; + } + + if (r_click_item != nullptr) { + *r_click_item = table_click_item; + } + + if (r_click_char != nullptr) { + *r_click_char = table_click_char; + } + return table_offy; + } + off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation")); } + // Text line hit. if (char_pos >= 0) { // Find item. if (r_click_item != nullptr) { @@ -1451,7 +1490,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V if (it_to != nullptr) { *r_click_item = _get_prev_item(it_to); } else { - for (Item *i = it; i && i != it_to; i = _get_next_item(i)) { + for (Item *i = it; i; i = _get_next_item(i)) { *r_click_item = i; } } @@ -1650,8 +1689,7 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_FOCUS_EXIT: { if (deselect_on_focus_loss_enabled) { - selection.active = false; - update(); + deselect(); } } break; @@ -1684,9 +1722,9 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const Item *item = nullptr; bool outside = true; - ((RichTextLabel *)(this))->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); + const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &item, nullptr, &outside); - if (item && !outside && ((RichTextLabel *)(this))->_find_meta(item, nullptr)) { + if (item && !outside && const_cast<RichTextLabel *>(this)->_find_meta(item, nullptr)) { return CURSOR_POINTING_HAND; } @@ -1742,9 +1780,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_line = 0; selection.to_item = nullptr; selection.to_char = 0; - selection.active = false; - - update(); + deselect(); } } } @@ -1802,9 +1838,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_line = 0; selection.to_item = nullptr; selection.to_char = 0; - selection.active = false; - - update(); + deselect(); } } @@ -1835,6 +1869,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8); } } + if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { + _generate_context_menu(); + menu->set_position(get_screen_position() + b->get_position()); + menu->reset_size(); + menu->popup(); + grab_focus(); + } } Ref<InputEventPanGesture> pan_gesture = p_event; @@ -1876,8 +1917,24 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { vscroll->set_value(vscroll->get_max()); handled = true; } - if (k->is_action("ui_copy")) { - selection_copy(); + if (is_shortcut_keys_enabled()) { + if (k->is_action("ui_text_select_all")) { + select_all(); + handled = true; + } + if (k->is_action("ui_copy")) { + selection_copy(); + handled = true; + } + } + if (k->is_action("ui_menu", true)) { + if (context_menu_enabled) { + _generate_context_menu(); + menu->set_position(get_screen_position()); + menu->reset_size(); + menu->popup(); + menu->grab_focus(); + } handled = true; } @@ -1919,14 +1976,13 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { selection.to_char = c_index; bool swap = false; - if (selection.from_item->index > selection.to_item->index) { - swap = true; - } else if (selection.from_item->index == selection.to_item->index) { - if (selection.from_char > selection.to_char) { + if (selection.click_frame && c_frame) { + const Line &l1 = c_frame->lines[c_line]; + const Line &l2 = selection.click_frame->lines[selection.click_line]; + if (l1.char_offset + c_index < l2.char_offset + selection.click_char) { swap = true; - } else if (selection.from_char == selection.to_char) { - selection.active = false; - update(); + } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char) { + deselect(); return; } } @@ -1988,7 +2044,7 @@ void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) while (item) { if (item->parent != nullptr && item->parent->type == ITEM_FRAME) { if (r_frame != nullptr) { - *r_frame = (ItemFrame *)item->parent; + *r_frame = static_cast<ItemFrame *>(item->parent); } if (r_line != nullptr) { *r_line = item->line; @@ -2192,7 +2248,7 @@ TextServer::Direction RichTextLabel::_find_direction(Item *p_item) { } } -Control::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) { +TextServer::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) { Item *item = p_item; while (item) { @@ -2523,7 +2579,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) p_item->index = current_idx++; p_item->char_ofs = current_char_ofs; if (p_item->type == ITEM_TEXT) { - ItemText *t = (ItemText *)p_item; + ItemText *t = static_cast<ItemText *>(p_item); current_char_ofs += t->text.length(); } else if (p_item->type == ITEM_IMAGE) { current_char_ofs++; @@ -2782,7 +2838,7 @@ void RichTextLabel::push_strikethrough() { _add_item(item, true); } -void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) { +void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, TextServer::StructuredTextParser p_st_parser) { ERR_FAIL_COND(current->type == ITEM_TABLE); ItemParagraph *item = memnew(ItemParagraph); @@ -2977,11 +3033,10 @@ void RichTextLabel::clear() { main->lines.clear(); main->lines.resize(1); main->first_invalid_line = 0; - update(); selection.click_frame = nullptr; selection.click_item = nullptr; - selection.active = false; + deselect(); current_idx = 1; current_char_ofs = 0; @@ -3122,7 +3177,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]; @@ -3408,7 +3463,7 @@ void RichTextLabel::append_text(const String &p_bbcode) { HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED; String lang; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; for (int i = 0; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("="); if (subtag_a.size() == 2) { @@ -3434,19 +3489,19 @@ void RichTextLabel::append_text(const String &p_bbcode) { lang = subtag_a[1]; } else if (subtag_a[0] == "st" || subtag_a[0] == "bidi_override") { if (subtag_a[1] == "d" || subtag_a[1] == "default") { - st_parser = STRUCTURED_TEXT_DEFAULT; + st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; } else if (subtag_a[1] == "u" || subtag_a[1] == "uri") { - st_parser = STRUCTURED_TEXT_URI; + st_parser = TextServer::STRUCTURED_TEXT_URI; } else if (subtag_a[1] == "f" || subtag_a[1] == "file") { - st_parser = STRUCTURED_TEXT_FILE; + st_parser = TextServer::STRUCTURED_TEXT_FILE; } else if (subtag_a[1] == "e" || subtag_a[1] == "email") { - st_parser = STRUCTURED_TEXT_EMAIL; + st_parser = TextServer::STRUCTURED_TEXT_EMAIL; } else if (subtag_a[1] == "l" || subtag_a[1] == "list") { - st_parser = STRUCTURED_TEXT_LIST; + st_parser = TextServer::STRUCTURED_TEXT_LIST; } else if (subtag_a[1] == "n" || subtag_a[1] == "none") { - st_parser = STRUCTURED_TEXT_NONE; + st_parser = TextServer::STRUCTURED_TEXT_NONE; } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") { - st_parser = STRUCTURED_TEXT_CUSTOM; + st_parser = TextServer::STRUCTURED_TEXT_CUSTOM; } } } @@ -3565,9 +3620,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; @@ -3581,14 +3636,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(); } } @@ -3674,15 +3729,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); @@ -3690,15 +3745,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); @@ -3707,15 +3762,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); @@ -3724,15 +3779,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); @@ -3741,21 +3796,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); @@ -3890,8 +3945,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { selection.enabled = p_enabled; if (!p_enabled) { if (selection.active) { - selection.active = false; - update(); + deselect(); } set_focus_mode(FOCUS_NONE); } else { @@ -3902,8 +3956,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) { void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { deselect_on_focus_loss_enabled = p_enabled; if (p_enabled && selection.active && !has_focus()) { - selection.active = false; - update(); + deselect(); } } @@ -3967,7 +4020,7 @@ bool RichTextLabel::_search_line(ItemFrame *p_frame, int p_line, const String &p text += "\n"; } break; case ITEM_TEXT: { - ItemText *t = (ItemText *)it; + ItemText *t = static_cast<ItemText *>(it); text += t->text; } break; case ITEM_IMAGE: { @@ -4100,7 +4153,7 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p if (it_to != nullptr) { end_idx = it_to->index; } else { - for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + for (Item *it = l.from; it; it = _get_next_item(it)) { end_idx = it->index + 1; } } @@ -4142,6 +4195,32 @@ String RichTextLabel::_get_line_text(ItemFrame *p_frame, int p_line, Selection p return text; } +void RichTextLabel::set_context_menu_enabled(bool p_enabled) { + context_menu_enabled = p_enabled; +} + +bool RichTextLabel::is_context_menu_enabled() const { + return context_menu_enabled; +} + +void RichTextLabel::set_shortcut_keys_enabled(bool p_enabled) { + shortcut_keys_enabled = p_enabled; +} + +bool RichTextLabel::is_shortcut_keys_enabled() const { + return shortcut_keys_enabled; +} + +// Context menu. +PopupMenu *RichTextLabel::get_menu() const { + const_cast<RichTextLabel *>(this)->_generate_context_menu(); + return menu; +} + +bool RichTextLabel::is_menu_visible() const { + return menu && menu->is_visible(); +} + String RichTextLabel::get_selected_text() const { if (!selection.active || !selection.enabled) { return ""; @@ -4154,6 +4233,11 @@ String RichTextLabel::get_selected_text() const { return text; } +void RichTextLabel::deselect() { + selection.active = false; + update(); +} + void RichTextLabel::selection_copy() { String text = get_selected_text(); @@ -4162,6 +4246,53 @@ void RichTextLabel::selection_copy() { } } +void RichTextLabel::select_all() { + if (!selection.enabled) { + return; + } + + Item *it = main; + Item *from_item = nullptr; + Item *to_item = nullptr; + + while (it) { + if (it->type != ITEM_FRAME) { + if (!from_item) { + from_item = it; + } else { + to_item = it; + } + } + it = _get_next_item(it, true); + } + if (!from_item || !to_item) { + return; + } + + ItemFrame *from_frame = nullptr; + int from_line = 0; + _find_frame(from_item, &from_frame, &from_line); + if (!from_frame) { + return; + } + ItemFrame *to_frame = nullptr; + int to_line = 0; + _find_frame(to_item, &to_frame, &to_line); + if (!to_frame) { + return; + } + selection.from_line = from_line; + selection.from_frame = from_frame; + selection.from_char = 0; + selection.from_item = from_item; + selection.to_line = to_line; + selection.to_frame = to_frame; + selection.to_char = to_frame->lines[to_line].char_count; + selection.to_item = to_item; + selection.active = true; + update(); +} + bool RichTextLabel::is_selection_enabled() const { return selection.enabled; } @@ -4218,10 +4349,8 @@ String RichTextLabel::get_parsed_text() const { Item *it = main; while (it) { if (it->type == ITEM_DROPCAP) { - const ItemDropcap *dc = (ItemDropcap *)it; - if (dc != nullptr) { - text += dc->text; - } + ItemDropcap *dc = static_cast<ItemDropcap *>(it); + text += dc->text; } else if (it->type == ITEM_TEXT) { ItemText *t = static_cast<ItemText *>(it); text += t->text; @@ -4247,7 +4376,7 @@ void RichTextLabel::set_text_direction(Control::TextDirection p_text_direction) } } -void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void RichTextLabel::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; main->first_invalid_line = 0; //invalidate ALL @@ -4256,7 +4385,7 @@ void RichTextLabel::set_structured_text_bidi_override(Control::StructuredTextPar } } -Control::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser RichTextLabel::get_structured_text_bidi_override() const { return st_parser; } @@ -4391,7 +4520,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color); ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size); ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color); - ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT)); + ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(TextServer::STRUCTURED_TEXT_DEFAULT)); ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent); ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list); ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta); @@ -4453,13 +4582,21 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled); ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled); + ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &RichTextLabel::set_context_menu_enabled); + ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &RichTextLabel::is_context_menu_enabled); + + ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &RichTextLabel::set_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &RichTextLabel::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &RichTextLabel::set_deselect_on_focus_loss_enabled); ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &RichTextLabel::is_deselect_on_focus_loss_enabled); ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from); ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to); + ClassDB::bind_method(D_METHOD("select_all"), &RichTextLabel::select_all); ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text); + ClassDB::bind_method(D_METHOD("deselect"), &RichTextLabel::deselect); ClassDB::bind_method(D_METHOD("parse_bbcode", "bbcode"), &RichTextLabel::parse_bbcode); ClassDB::bind_method(D_METHOD("append_text", "bbcode"), &RichTextLabel::append_text); @@ -4500,6 +4637,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); + ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu); + ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible); + // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); @@ -4521,6 +4661,9 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_GROUP("Locale", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); @@ -4700,6 +4843,59 @@ Size2 RichTextLabel::get_minimum_size() const { return size; } +// Context menu. +void RichTextLabel::_generate_context_menu() { + if (!menu) { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option)); + } + + // Reorganize context menu. + menu->clear(); + if (selection.enabled) { + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); + } +} + +Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) { + const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); + if (!events) { + return Key::NONE; + } + + // Use first event in the list for the accelerator. + const List<Ref<InputEvent>>::Element *first_event = events->front(); + if (!first_event) { + return Key::NONE; + } + + const Ref<InputEventKey> event = first_event->get(); + if (event.is_null()) { + return Key::NONE; + } + + // Use physical keycode if non-zero + if (event->get_physical_keycode() != Key::NONE) { + return event->get_physical_keycode_with_modifiers(); + } else { + return event->get_keycode_with_modifiers(); + } +} + +void RichTextLabel::_menu_option(int p_option) { + switch (p_option) { + case MENU_COPY: { + selection_copy(); + } break; + case MENU_SELECT_ALL: { + select_all(); + } break; + } +} + void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) { Vector2i fbg_index = Vector2i(end, start); Color last_color = Color(0, 0, 0, 0); @@ -4707,7 +4903,7 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item // Draw a box based on color tags associated with glyphs for (int i = start; i < end; i++) { Item *it = _get_item_at_pos(it_from, it_to, i); - Color color = Color(0, 0, 0, 0); + Color color; if (fbg_flag == 0) { color = _find_bgcolor(it); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index c9cbbe9d15..c6d0d0875d 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -32,6 +32,7 @@ #define RICH_TEXT_LABEL_H #include "rich_text_effect.h" +#include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" #include "scene/resources/text_paragraph.h" @@ -91,6 +92,11 @@ public: VC_GLYPHS_RTL, }; + enum MenuItems { + MENU_COPY, + MENU_SELECT_ALL, + }; + protected: void _notification(int p_what); static void _bind_methods(); @@ -228,7 +234,7 @@ private: HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; String language; Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; ItemParagraph() { type = ITEM_PARAGRAPH; } }; @@ -393,7 +399,7 @@ private: String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; struct Selection { @@ -420,6 +426,15 @@ private: Selection selection; bool deselect_on_focus_loss_enabled = true; + bool context_menu_enabled = false; + bool shortcut_keys_enabled = true; + + // Context menu. + PopupMenu *menu = nullptr; + void _generate_context_menu(); + Key _get_menu_action_accelerator(const String &p_action); + void _menu_option(int p_option); + int visible_characters = -1; float percent_visible = 1.0; VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING; @@ -435,7 +450,7 @@ private: void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width); 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); + 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); String _roman(int p_num, bool p_capitalize) const; String _letters(int p_num, bool p_capitalize) const; @@ -452,7 +467,7 @@ private: int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); HorizontalAlignment _find_alignment(Item *p_item); TextServer::Direction _find_direction(Item *p_item); - Control::StructuredTextParser _find_stt(Item *p_item); + TextServer::StructuredTextParser _find_stt(Item *p_item); String _find_language(Item *p_item); Color _find_color(Item *p_item, const Color &p_default_color); Color _find_outline_color(Item *p_item, const Color &p_default_color); @@ -510,7 +525,7 @@ public: void push_outline_color(const Color &p_color); void push_underline(); void push_strikethrough(); - void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT); + void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT); void push_indent(int p_level); void push_list(int p_level, ListType p_list, bool p_capitalize); void push_meta(const Variant &p_meta); @@ -555,6 +570,12 @@ public: void set_tab_size(int p_spaces); int get_tab_size() const; + void set_context_menu_enabled(bool p_enabled); + bool is_context_menu_enabled() const; + + void set_shortcut_keys_enabled(bool p_enabled); + bool is_shortcut_keys_enabled() const; + void set_fit_content_height(bool p_enabled); bool is_fit_content_height_enabled() const; @@ -584,9 +605,15 @@ public: int get_selection_from() const; int get_selection_to() const; String get_selected_text() const; + void select_all(); void selection_copy(); void set_deselect_on_focus_loss_enabled(const bool p_enabled); bool is_deselect_on_focus_loss_enabled() const; + void deselect(); + + // Context menu. + PopupMenu *get_menu() const; + bool is_menu_visible() const; void parse_bbcode(const String &p_bbcode); void append_text(const String &p_bbcode); @@ -606,8 +633,8 @@ public: void set_autowrap_mode(AutowrapMode p_mode); AutowrapMode get_autowrap_mode() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() 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; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index c00df87b18..b9fcf64db6 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -47,8 +47,8 @@ public: }; private: - HScrollBar *h_scroll; - VScrollBar *v_scroll; + HScrollBar *h_scroll = nullptr; + VScrollBar *v_scroll = nullptr; Size2 child_max_size; diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index a15e3fe5f5..d118b28334 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -38,11 +38,11 @@ class SpinBox : public Range { GDCLASS(SpinBox, Range); - LineEdit *line_edit; + LineEdit *line_edit = nullptr; int last_w = 0; bool update_on_text_changed = false; - Timer *range_click_timer; + Timer *range_click_timer = nullptr; void _range_click_timeout(); void _release_mouse(); diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index ce2dca0ea3..b96ba0ebf9 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -49,7 +49,7 @@ Size2 TabBar::get_minimum_size() const { Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); Ref<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight")); Ref<Texture2D> close = get_theme_icon(SNAME("close")); - int hseparation = get_theme_constant(SNAME("hseparation")); + int hseparation = get_theme_constant(SNAME("h_separation")); int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); @@ -477,7 +477,7 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in Color font_outline_color = get_theme_color(SNAME("font_outline_color")); int outline_size = get_theme_constant(SNAME("outline_size")); - int hseparation = get_theme_constant(SNAME("hseparation")); + int hseparation = get_theme_constant(SNAME("h_separation")); Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height); p_tab_style->draw(ci, sb_rect); @@ -1272,7 +1272,7 @@ int TabBar::get_tab_width(int p_idx) const { Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected")); Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected")); Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled")); - int hseparation = get_theme_constant(SNAME("hseparation")); + int hseparation = get_theme_constant(SNAME("h_separation")); Ref<StyleBox> style; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 05f1ee3dad..8299d73b68 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -208,8 +208,8 @@ void TabContainer::_on_theme_changed() { tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color"))); tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color"))); tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font"))); - tab_bar->add_theme_constant_override(SNAME("font_size"), get_theme_constant(SNAME("font_size"))); - tab_bar->add_theme_constant_override(SNAME("hseparation"), get_theme_constant(SNAME("icon_separation"))); + tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size"))); + tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation"))); tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size"))); _update_margins(); @@ -472,6 +472,10 @@ void TabContainer::_on_tab_selected(int p_tab) { emit_signal(SNAME("tab_selected"), p_tab); } +void TabContainer::_on_tab_button_pressed(int p_tab) { + emit_signal(SNAME("tab_button_pressed"), p_tab); +} + void TabContainer::_refresh_tab_names() { Vector<Control *> controls = _get_tab_controls(); for (int i = 0; i < controls.size(); i++) { @@ -519,7 +523,7 @@ void TabContainer::move_child_notify(Node *p_child) { Control *c = Object::cast_to<Control>(p_child); if (c && !c->is_set_as_top_level()) { int old_idx = -1; - String tab_name = c->has_meta("_tab_name") ? String(c->get_meta("_tab_name")) : String(c->get_name()); + String tab_name = String(c->get_meta("_tab_name", c->get_name())); // Find the previous tab index of the control. for (int i = 0; i < get_tab_count(); i++) { @@ -552,9 +556,7 @@ void TabContainer::remove_child_notify(Node *p_child) { update(); } - if (p_child->has_meta("_tab_name")) { - p_child->remove_meta("_tab_name"); - } + p_child->remove_meta("_tab_name"); p_child->disconnect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names)); // TabBar won't emit the "tab_changed" signal when not inside the tree. @@ -675,9 +677,7 @@ void TabContainer::set_tab_title(int p_tab, const String &p_title) { tab_bar->set_tab_title(p_tab, p_title); if (p_title == child->get_name()) { - if (child->has_meta("_tab_name")) { - child->remove_meta("_tab_name"); - } + child->remove_meta("_tab_name"); } else { child->set_meta("_tab_name", p_title); } @@ -733,6 +733,17 @@ bool TabContainer::is_tab_hidden(int p_tab) const { return tab_bar->is_tab_hidden(p_tab); } +void TabContainer::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) { + tab_bar->set_tab_button_icon(p_tab, p_icon); + + _update_margins(); + _repaint(); +} + +Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const { + return tab_bar->get_tab_button_icon(p_tab); +} + void TabContainer::get_translatable_strings(List<String> *p_strings) const { Vector<Control *> controls = _get_tab_controls(); for (int i = 0; i < controls.size(); i++) { @@ -877,6 +888,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled); ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden); + ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon); + ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon); ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point); ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); @@ -896,6 +909,7 @@ void TabContainer::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("pre_popup_pressed")); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment"); @@ -915,6 +929,7 @@ TabContainer::TabContainer() { tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE); tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed)); tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected)); + tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed)); connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited)); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index c54934b37b..9adaa0d844 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -38,7 +38,7 @@ class TabContainer : public Container { GDCLASS(TabContainer, Container); - TabBar *tab_bar; + TabBar *tab_bar = nullptr; bool tabs_visible = true; bool all_tabs_in_front = false; bool menu_hovered = false; @@ -56,6 +56,7 @@ class TabContainer : public Container { void _on_mouse_exited(); void _on_tab_changed(int p_tab); void _on_tab_selected(int p_tab); + void _on_tab_button_pressed(int p_tab); Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control); bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const; @@ -97,6 +98,9 @@ public: void set_tab_hidden(int p_tab, bool p_hidden); bool is_tab_hidden(int p_tab) const; + void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_tab_button_icon(int p_tab) const; + int get_tab_count() const; void set_current_tab(int p_current); int get_current_tab() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 3c80e3f987..315ffbd419 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -471,6 +471,11 @@ void TextEdit::_notification(int p_what) { // To ensure minimap is responsive override the speed setting. double vel = ((target_y / dist) * ((minimap_clicked) ? 3000 : v_scroll_speed)) * get_physics_process_delta_time(); + // Prevent too small velocity to block scrolling + if (Math::abs(vel) < v_scroll->get_step()) { + vel = v_scroll->get_step() * SIGN(vel); + } + if (Math::abs(vel) >= dist) { set_v_scroll(target_v_scroll); scrolling = false; @@ -1423,7 +1428,7 @@ void TextEdit::_notification(int p_what) { } } - if (draw_placeholder) { + if (!draw_placeholder) { line_drawing_cache[line] = cache_entry; } } @@ -1537,6 +1542,62 @@ void TextEdit::_notification(int p_what) { } } +void TextEdit::unhandled_key_input(const Ref<InputEvent> &p_event) { + Ref<InputEventKey> k = p_event; + + if (k.is_valid()) { + if (!k->is_pressed()) { + return; + } + // Handle Unicode (with modifiers active, process after shortcuts). + if (has_focus() && editable && (k->get_unicode() >= 32)) { + handle_unicode_input(k->get_unicode()); + accept_event(); + } + } +} + +bool TextEdit::alt_input(const Ref<InputEvent> &p_gui_input) { + Ref<InputEventKey> k = p_gui_input; + if (k.is_valid()) { + if (!k->is_pressed()) { + if (alt_start && k->get_keycode() == Key::ALT) { + alt_start = false; + if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) { + handle_unicode_input(alt_code); + } + return true; + } + return false; + } + + if (k->is_alt_pressed()) { + if (!alt_start) { + if (k->get_keycode() == Key::KP_ADD) { + alt_start = true; + alt_code = 0; + return true; + } + } else { + if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0); + } + if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::KP_0); + } + if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) { + alt_code = alt_code << 4; + alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10; + } + return true; + } + } + } + return false; +} + void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -1620,7 +1681,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { set_caret_column(col); selection.drag_attempt = false; - if (mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) { + if (selecting_enabled && mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) { if (!selection.active) { selection.active = true; selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER; @@ -1688,7 +1749,6 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { last_dblclk = OS::get_singleton()->get_ticks_msec(); last_dblclk_pos = mb->get_position(); } - update(); } @@ -1696,7 +1756,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { paste_primary_clipboard(); } - if (mb->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { + if (mb->get_button_index() == MouseButton::RIGHT && (context_menu_enabled || is_move_caret_on_right_click_enabled())) { _reset_caret_blink_timer(); Point2i pos = get_line_column_at_pos(mpos); @@ -1721,11 +1781,13 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } } - _generate_context_menu(); - menu->set_position(get_screen_position() + mpos); - menu->reset_size(); - menu->popup(); - grab_focus(); + if (context_menu_enabled) { + _generate_context_menu(); + menu->set_position(get_screen_position() + mpos); + menu->reset_size(); + menu->popup(); + grab_focus(); + } } } else { if (mb->get_button_index() == MouseButton::LEFT) { @@ -1844,6 +1906,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { Ref<InputEventKey> k = p_gui_input; if (k.is_valid()) { + if (alt_input(p_gui_input)) { + accept_event(); + return; + } if (!k->is_pressed()) { return; } @@ -1924,44 +1990,45 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } - // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE. - - if (k->is_action("ui_text_select_all", true)) { - select_all(); - accept_event(); - return; - } - if (k->is_action("ui_text_select_word_under_caret", true)) { - select_word_under_caret(); - accept_event(); - return; - } - if (k->is_action("ui_cut", true)) { - cut(); - accept_event(); - return; - } - if (k->is_action("ui_copy", true)) { - copy(); - accept_event(); - return; - } - if (k->is_action("ui_paste", true)) { - paste(); - accept_event(); - return; - } + if (is_shortcut_keys_enabled()) { + // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE. + if (k->is_action("ui_text_select_all", true)) { + select_all(); + accept_event(); + return; + } + if (k->is_action("ui_text_select_word_under_caret", true)) { + select_word_under_caret(); + accept_event(); + return; + } + if (k->is_action("ui_cut", true)) { + cut(); + accept_event(); + return; + } + if (k->is_action("ui_copy", true)) { + copy(); + accept_event(); + return; + } + if (k->is_action("ui_paste", true)) { + paste(); + accept_event(); + return; + } - // UNDO/REDO. - if (k->is_action("ui_undo", true)) { - undo(); - accept_event(); - return; - } - if (k->is_action("ui_redo", true)) { - redo(); - accept_event(); - return; + // UNDO/REDO. + if (k->is_action("ui_undo", true)) { + undo(); + accept_event(); + return; + } + if (k->is_action("ui_redo", true)) { + redo(); + accept_event(); + return; + } } // MISC. @@ -2133,16 +2200,21 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) { if (p_move_by_word) { int cc = caret.column; - + // If the caret is at the start of the line, and not on the first line, move it up to the end of the previous line. if (cc == 0 && caret.line > 0) { set_caret_line(caret.line - 1); set_caret_column(text[caret.line].length()); } else { PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = words.size() - 2; i >= 0; i = i - 2) { - if (words[i] < cc) { - cc = words[i]; - break; + if (words.is_empty() || cc <= words[0]) { + // This solves the scenario where there are no words but glyfs that can be ignored. + cc = 0; + } else { + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < cc) { + cc = words[i]; + break; + } } } set_caret_column(cc); @@ -2184,16 +2256,21 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) { if (p_move_by_word) { int cc = caret.column; - + // If the caret is at the end of the line, and not on the last line, move it down to the beginning of the next line. if (cc == text[caret.line].length() && caret.line < text.size() - 1) { set_caret_line(caret.line + 1); set_caret_column(0); } else { PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); - for (int i = 1; i < words.size(); i = i + 2) { - if (words[i] > cc) { - cc = words[i]; - break; + if (words.is_empty() || cc >= words[words.size() - 1]) { + // This solves the scenario where there are no words but glyfs that can be ignored. + cc = text[caret.line].length(); + } else { + for (int i = 1; i < words.size(); i = i + 2) { + if (words[i] > cc) { + cc = words[i]; + break; + } } } set_caret_column(cc); @@ -2283,15 +2360,7 @@ void TextEdit::_move_caret_to_line_start(bool p_select) { } if (caret.column == row_start_col || wi == 0) { // Compute whitespace symbols sequence length. - int current_line_whitespace_len = 0; - while (current_line_whitespace_len < text[caret.line].length()) { - char32_t c = text[caret.line][current_line_whitespace_len]; - if (c != '\t' && c != ' ') { - break; - } - current_line_whitespace_len++; - } - + int current_line_whitespace_len = get_first_non_whitespace_column(caret.line); if (get_caret_column() == current_line_whitespace_len) { set_caret_column(0); } else { @@ -2364,11 +2433,11 @@ void TextEdit::_move_caret_page_down(bool p_select) { } void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { - if (!editable) { + if (!editable || (caret.column == 0 && caret.line == 0 && !has_selection())) { return; } - if (has_selection() || (!p_all_to_left && !p_word)) { + if (has_selection() || (!p_all_to_left && !p_word) || caret.column == 0) { backspace(); return; } @@ -2381,20 +2450,30 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { } if (p_word) { - int line = caret.line; int column = caret.column; - - PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); - for (int i = words.size() - 2; i >= 0; i = i - 2) { - if (words[i] < column) { - column = words[i]; - break; + // Check for the case "<word><space><caret>" and ignore the space. + // No need to check for column being 0 since it is cheked above. + if (is_whitespace(text[caret.line][caret.column - 1])) { + column -= 1; + } + // Get a list with the indices of the word bounds of the given text line. + const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); + if (words.is_empty() || column <= words[0]) { + // If "words" is empty, meaning no words are left, we can remove everything until the begining of the line. + column = 0; + } else { + // Otherwise search for the first word break that is smaller than the index from we're currentlu deleteing + for (int i = words.size() - 2; i >= 0; i = i - 2) { + if (words[i] < column) { + column = words[i]; + break; + } } } - _remove_text(line, column, caret.line, caret.column); + _remove_text(caret.line, column, caret.line, caret.column); - set_caret_line(line, false); + set_caret_line(caret.line, false); set_caret_column(column); return; } @@ -2419,6 +2498,10 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) { int next_column; if (p_all_to_right) { + if (caret.column == curline_len) { + return; + } + // Delete everything to right of caret next_column = curline_len; next_line = caret.line; @@ -2818,7 +2901,7 @@ String TextEdit::get_language() const { return language; } -void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void TextEdit::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; for (int i = 0; i < text.size(); i++) { @@ -2828,7 +2911,7 @@ void TextEdit::set_structured_text_bidi_override(Control::StructuredTextParser p } } -Control::StructuredTextParser TextEdit::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser TextEdit::get_structured_text_bidi_override() const { return st_parser; } @@ -3081,6 +3164,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) { ++selection.to_line; } } + update(); } void TextEdit::insert_text_at_caret(const String &p_text) { @@ -3461,9 +3545,6 @@ void TextEdit::undo() { TextOperation op = undo_stack_pos->get(); _do_text_op(op, true); - if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) { - select(op.from_line, op.from_column, op.to_line, op.to_column); - } current_op.version = op.prev_version; if (undo_stack_pos->get().chain_backward) { @@ -3479,6 +3560,10 @@ void TextEdit::undo() { } } + if (op.type != TextOperation::TYPE_INSERT && (op.from_line != op.to_line || op.to_column != op.from_column + 1)) { + select(op.from_line, op.from_column, op.to_line, op.to_column); + } + _update_scrollbars(); if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) { set_caret_line(undo_stack_pos->get().to_line, false); @@ -3775,7 +3860,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_ Point2i TextEdit::get_pos_at_line_column(int p_line, int p_column) const { Rect2i rect = get_rect_at_line_column(p_line, p_column); - return rect.position + Vector2i(0, get_line_height()); + return rect.position.x == -1 ? rect.position : rect.position + Vector2i(0, get_line_height()); } Rect2i TextEdit::get_rect_at_line_column(int p_line, int p_column) const { @@ -4013,12 +4098,12 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) { if (p_col < 0) { p_col = 0; } + if (p_col > get_line(caret.line).length()) { + p_col = get_line(caret.line).length(); + } bool caret_moved = caret.column != p_col; caret.column = p_col; - if (caret.column > get_line(caret.line).length()) { - caret.column = get_line(caret.line).length(); - } caret.last_fit_x = _get_column_x_offset_for_line(caret.column, caret.line); @@ -4147,13 +4232,18 @@ void TextEdit::select_word_under_caret() { int end = 0; const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i = i + 2) { - if ((words[i] < caret.column && words[i + 1] > caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) { + if ((words[i] <= caret.column && words[i + 1] >= caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) { begin = words[i]; end = words[i + 1]; break; } } + // No word found. + if (begin == 0 && end == 0) { + return; + } + select(caret.line, begin, caret.line, end); /* Move the caret to the end of the word for easier editing. */ set_caret_column(end, false); @@ -4229,10 +4319,12 @@ String TextEdit::get_selected_text() const { } int TextEdit::get_selection_line() const { + ERR_FAIL_COND_V(!selection.active, -1); return selection.selecting_line; } int TextEdit::get_selection_column() const { + ERR_FAIL_COND_V(!selection.active, -1); return selection.selecting_column; } @@ -4390,6 +4482,8 @@ int TextEdit::get_h_scroll() const { } void TextEdit::set_v_scroll_speed(float p_speed) { + // Prevent setting a vertical scroll speed value under 1.0 + ERR_FAIL_COND(p_speed < 1.0); v_scroll_speed = p_speed; } @@ -4432,9 +4526,13 @@ void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) { ERR_FAIL_COND(p_wrap_index > get_line_wrap_count(p_line)); int visible_rows = get_visible_line_count(); - Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -visible_rows / 2); + Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, (-visible_rows / 2) - 1); int first_line = p_line - next_line.x + 1; + if (first_line < 0) { + set_v_scroll(0); + return; + } set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y)); } @@ -4446,6 +4544,12 @@ void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) { Point2i next_line = get_next_visible_line_index_offset_from(p_line, p_wrap_index, -get_visible_line_count() - 1); int first_line = p_line - next_line.x + 1; + // Adding _get_visible_lines_offset is not 100% correct as we end up showing almost p_line + 1, however, it provides a + // better user experience. Therefore we need to special case < visible line count, else showing line 0 is impossible. + if (get_visible_line_count_in_range(0, p_line) < get_visible_line_count() + 1) { + set_v_scroll(0); + return; + } set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y) + _get_visible_lines_offset()); } @@ -5855,7 +5959,7 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const { int row = 0; Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line); for (int i = 0; i < rows2.size(); i++) { - if ((p_char >= rows2[i].x) && (p_char < rows2[i].y)) { + if ((p_char >= rows2[i].x) && (p_char <= rows2[i].y)) { row = i; break; } @@ -5939,8 +6043,8 @@ void TextEdit::_update_selection_mode_word() { selection.selected_word_beg = beg; selection.selected_word_end = end; selection.selected_word_origin = beg; - set_caret_line(selection.to_line, false); - set_caret_column(selection.to_column); + set_caret_line(line, false); + set_caret_column(end); } else { if ((col <= selection.selected_word_origin && line == selection.selecting_line) || line < selection.selecting_line) { selection.selecting_column = selection.selected_word_end; @@ -5996,6 +6100,10 @@ void TextEdit::_update_selection_mode_line() { } void TextEdit::_pre_shift_selection() { + if (!selecting_enabled) { + return; + } + if (!selection.active || selection.selecting_mode == SelectionMode::SELECTION_MODE_NONE) { selection.selecting_line = caret.line; selection.selecting_column = caret.column; @@ -6006,6 +6114,10 @@ void TextEdit::_pre_shift_selection() { } void TextEdit::_post_shift_selection() { + if (!selecting_enabled) { + return; + } + if (selection.active && selection.selecting_mode == SelectionMode::SELECTION_MODE_SHIFT) { select(selection.selecting_line, selection.selecting_column, caret.line, caret.column); update(); @@ -6592,6 +6704,7 @@ TextEdit::TextEdit(const String &p_placeholder) { set_focus_mode(FOCUS_ALL); _update_caches(); set_default_cursor_shape(CURSOR_IBEAM); + set_process_unhandled_key_input(true); text.set_tab_size(text.get_tab_size()); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 6deaf76e5e..993203bee6 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -247,6 +247,9 @@ private: bool setting_text = false; + bool alt_start = false; + uint32_t alt_code = 0; + // Text properties. String ime_text = ""; Point2 ime_selection; @@ -271,7 +274,7 @@ private: Dictionary opentype_features; String language = ""; - Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; void _clear(); @@ -325,7 +328,7 @@ private: List<TextOperation> undo_stack; List<TextOperation>::Element *undo_stack_pos = nullptr; - Timer *idle_detect; + Timer *idle_detect = nullptr; uint32_t version = 0; uint32_t saved_version = 0; @@ -353,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; @@ -380,7 +383,7 @@ private: bool draw_caret = true; bool caret_blink_enabled = false; - Timer *caret_blink_timer; + Timer *caret_blink_timer = nullptr; bool move_caret_on_right_click = true; @@ -426,7 +429,7 @@ private: bool dragging_selection = false; - Timer *click_select_held; + Timer *click_select_held = nullptr; uint64_t last_dblclk = 0; Vector2 last_dblclk_pos; void _click_selection_held(); @@ -449,8 +452,8 @@ private: void _update_caret_wrap_offset(); /* Viewport. */ - HScrollBar *h_scroll; - VScrollBar *v_scroll; + HScrollBar *h_scroll = nullptr; + VScrollBar *v_scroll = nullptr; bool scroll_past_end_of_file_enabled = false; @@ -508,7 +511,6 @@ private: /* Syntax highlighting. */ Ref<SyntaxHighlighter> syntax_highlighter; - Map<int, Dictionary> syntax_highlighting_cache; Dictionary _get_line_syntax_highlighting(int p_line); @@ -623,7 +625,9 @@ protected: public: /* General overrides. */ + virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; virtual void gui_input(const Ref<InputEvent> &p_gui_input) override; + bool alt_input(const Ref<InputEvent> &p_gui_input); virtual Size2 get_minimum_size() const override; virtual bool is_text_field() const override; virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; @@ -650,8 +654,8 @@ public: void set_language(const String &p_language); String get_language() const; - void set_structured_text_bidi_override(Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override() 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; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index ccd24ed2cf..0ca9a66e08 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -101,6 +101,7 @@ void TreeItem::_change_tree(Tree *p_tree) { if (tree->popup_edited_item == this) { tree->popup_edited_item = nullptr; + tree->popup_pressing_edited_item = nullptr; tree->pressing_for_editor = false; } @@ -333,7 +334,7 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { return cells[p_column].opentype_features[tag]; } -void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) { +void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); if (cells[p_column].st_parser != p_parser) { @@ -345,8 +346,8 @@ void TreeItem::set_structured_text_bidi_override(int p_column, Control::Structur } } -Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const { - ERR_FAIL_INDEX_V(p_column, cells.size(), Control::STRUCTURED_TEXT_NONE); +TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE); return cells[p_column].st_parser; } @@ -1411,8 +1412,8 @@ void Tree::update_cache() { cache.font_color = get_theme_color(SNAME("font_color")); cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); cache.drop_position_color = get_theme_color(SNAME("drop_position_color")); - cache.hseparation = get_theme_constant(SNAME("hseparation")); - cache.vseparation = get_theme_constant(SNAME("vseparation")); + cache.hseparation = get_theme_constant(SNAME("h_separation")); + cache.vseparation = get_theme_constant(SNAME("v_separation")); cache.item_margin = get_theme_constant(SNAME("item_margin")); cache.button_margin = get_theme_constant(SNAME("button_margin")); @@ -1753,19 +1754,16 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[i].buttons[j].texture; Size2 s = b->get_size() + cache.button_pressed->get_minimum_size(); - if (s.height < label_h) { - s.height = label_h; - } Point2i o = Point2i(ofs + w - s.width, p_pos.y) - cache.offset + p_draw_ofs; if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) { - //being pressed + // Being pressed. Point2 od = o; if (rtl) { od.x = get_size().width - od.x - s.x; } - cache.button_pressed->draw(get_canvas_item(), Rect2(od, s)); + cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, s.width, MAX(s.height, label_h))); } o.y += (label_h - s.height) / 2; @@ -2088,7 +2086,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->first_child; int base_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; @@ -2096,82 +2093,97 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int prev_hl_ofs = base_ofs; while (c) { + int child_h = -1; if (htotal >= 0) { - int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); + child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c); + } - // Draw relationship lines. - if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) { - int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); - int parent_ofs = p_pos.x + cache.item_margin; - Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; + // Draw relationship lines. + if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) { + int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); + int parent_ofs = p_pos.x + cache.item_margin; + Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - if (c->get_first_child() != nullptr) { - root_pos -= Point2i(cache.arrow->get_width(), 0); - } + if (c->get_first_child() != nullptr) { + root_pos -= Point2i(cache.arrow->get_width(), 0); + } - float line_width = cache.relationship_line_width * Math::round(cache.base_scale); - float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale); - float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale); + float line_width = cache.relationship_line_width * Math::round(cache.base_scale); + float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale); + float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale); - Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; + Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs; - int more_prev_ofs = 0; + int more_prev_ofs = 0; - if (root_pos.y + line_width >= 0) { - if (rtl) { - root_pos.x = get_size().width - root_pos.x; - parent_pos.x = get_size().width - parent_pos.x; - } + if (root_pos.y + line_width >= 0) { + if (rtl) { + root_pos.x = get_size().width - root_pos.x; + parent_pos.x = get_size().width - parent_pos.x; + } - // Order of parts on this bend: the horizontal line first, then the vertical line. - if (_is_branch_selected(c)) { - // If this item or one of its children is selected, we draw the line using parent highlight style. + // Order of parts on this bend: the horizontal line first, then the vertical line. + if (_is_branch_selected(c)) { + // If this item or one of its children is selected, we draw the line using parent highlight style. + if (htotal >= 0) { RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width); + } + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + + more_prev_ofs = cache.parent_hl_line_margin; + prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); + } else if (p_item->is_selected(0)) { + // If parent item is selected (but this item is not), we draw the line using children highlight style. + // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. + if (_is_sibling_branch_selected(c)) { + if (htotal >= 0) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); + } RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); - more_prev_ofs = cache.parent_hl_line_margin; prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); - } else if (p_item->is_selected(0)) { - // If parent item is selected (but this item is not), we draw the line using children highlight style. - // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. - if (_is_sibling_branch_selected(c)) { - RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); - - prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); - } else { + } else { + if (htotal >= 0) { RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); } - } else { - // If nothing of the above is true, we draw the line using normal style. - // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. - if (_is_sibling_branch_selected(c)) { + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width); + } + } else { + // If nothing of the above is true, we draw the line using normal style. + // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted. + if (_is_sibling_branch_selected(c)) { + if (htotal >= 0) { RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); + } + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width); - prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); - } else { + prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2); + } else { + if (htotal >= 0) { RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width); - RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); } + RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width); } } - - prev_ofs = root_pos.y + more_prev_ofs; } - if (child_h < 0) { - if (cache.draw_relationship_lines == 0) { - return -1; // break, stop drawing, no need to anymore - } + prev_ofs = root_pos.y + more_prev_ofs; + } - htotal = -1; - children_pos.y = cache.offset.y + p_draw_size.height; - } else { - htotal += child_h; - children_pos.y += child_h; + if (child_h < 0) { + if (htotal == -1) { + break; // Last loop done, stop. + } + + if (cache.draw_relationship_lines == 0) { + return -1; // No need to draw anymore, full stop. } + + htotal = -1; + children_pos.y = cache.offset.y + p_draw_size.height; + } else { + htotal += child_h; + children_pos.y += child_h; } c = c->next; @@ -2670,8 +2682,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } click_handled = true; - popup_edited_item = p_item; - popup_edited_item_col = col; + popup_pressing_edited_item = p_item; + popup_pressing_edited_item_column = col; pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - cache.offset, Size2(col_width, item_h)); pressing_for_editor_text = editor_text; @@ -3206,10 +3218,16 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { update(); } - if (pressing_for_editor && popup_edited_item && (popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_RANGE)) { - //range drag + if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) { + /* This needs to happen now, because the popup can be closed when pressing another item, and must remain the popup edited item until it actually closes */ + popup_edited_item = popup_pressing_edited_item; + popup_edited_item_col = popup_pressing_edited_item_column; + + popup_pressing_edited_item = nullptr; + popup_pressing_edited_item_column = -1; if (!range_drag_enabled) { + //range drag Vector2 cpos = mm->get_position(); if (rtl) { cpos.x = get_size().width - cpos.x; @@ -3994,6 +4012,7 @@ void Tree::clear() { selected_item = nullptr; edited_item = nullptr; popup_edited_item = nullptr; + popup_pressing_edited_item = nullptr; update(); }; @@ -4309,12 +4328,16 @@ int Tree::get_pressed_button() const { return pressed_button; } -Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column) const { +Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const { ERR_FAIL_NULL_V(p_item, Rect2()); ERR_FAIL_COND_V(p_item->tree != this, Rect2()); if (p_column != -1) { ERR_FAIL_INDEX_V(p_column, columns.size(), Rect2()); } + if (p_button != -1) { + ERR_FAIL_COND_V(p_column == -1, Rect2()); // pass a column if you want to pass a button + ERR_FAIL_INDEX_V(p_button, p_item->cells[p_column].buttons.size(), Rect2()); + } int ofs = get_item_offset(p_item); int height = compute_item_height(p_item); @@ -4332,6 +4355,19 @@ Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column) const { } r.position.x = accum; r.size.x = get_column_width(p_column); + if (p_button != -1) { + const TreeItem::Cell &c = p_item->cells[p_column]; + Vector2 ofst = Vector2(r.position.x + r.size.x, r.position.y); + for (int j = c.buttons.size() - 1; j >= 0; j--) { + Ref<Texture2D> b = c.buttons[j].texture; + Size2 size = b->get_size() + cache.button_pressed->get_minimum_size(); + ofst.x -= size.x; + + if (j == p_button) { + return Rect2(ofst, size); + } + } + } } return r; @@ -4870,7 +4906,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column); ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected); ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect); - ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column"), &Tree::get_item_rect, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column", "button_index"), &Tree::get_item_rect, DEFVAL(-1), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position); ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position); ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 04d4b9b935..8ee2a3c382 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -65,7 +65,7 @@ private: Ref<TextLine> text_buf; Dictionary opentype_features; String language; - Control::StructuredTextParser st_parser = Control::STRUCTURED_TEXT_DEFAULT; + TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; bool dirty = true; @@ -134,7 +134,7 @@ private: Vector<TreeItem *> children_cache; bool is_root = false; // for tree root - Tree *tree; // tree (for reference) + Tree *tree = nullptr; // tree (for reference) TreeItem(Tree *p_tree); @@ -220,8 +220,8 @@ public: int get_opentype_feature(int p_column, const String &p_name) const; void clear_opentype_features(int p_column); - void set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser); - Control::StructuredTextParser get_structured_text_bidi_override(int p_column) const; + void set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser); + TextServer::StructuredTextParser get_structured_text_bidi_override(int p_column) const; void set_structured_text_bidi_override_options(int p_column, Array p_args); Array get_structured_text_bidi_override_options(int p_column) const; @@ -379,6 +379,9 @@ private: TreeItem *selected_item = nullptr; TreeItem *edited_item = nullptr; + TreeItem *popup_pressing_edited_item = nullptr; // Candidate. + int popup_pressing_edited_item_column = -1; + TreeItem *drop_mode_over = nullptr; int drop_mode_section = 0; @@ -428,18 +431,18 @@ private: bool show_column_titles = false; - VBoxContainer *popup_editor_vb; + VBoxContainer *popup_editor_vb = nullptr; - Popup *popup_editor; + Popup *popup_editor = nullptr; LineEdit *text_editor = nullptr; - HSlider *value_editor; + HSlider *value_editor = nullptr; bool updating_value_editor = false; uint64_t focus_in_id = 0; PopupMenu *popup_menu = nullptr; Vector<ColumnInfo> columns; - Timer *range_click_timer; + Timer *range_click_timer = nullptr; TreeItem *range_item_last = nullptr; bool range_up_last = false; void _range_click_timeout(); @@ -553,8 +556,8 @@ private: int _get_title_button_height() const; void _scroll_moved(float p_value); - HScrollBar *h_scroll; - VScrollBar *v_scroll; + HScrollBar *h_scroll = nullptr; + VScrollBar *v_scroll = nullptr; bool h_scroll_enabled = true; bool v_scroll_enabled = true; @@ -673,7 +676,7 @@ public: Rect2 get_custom_popup_rect() const; int get_item_offset(TreeItem *p_item) const; - Rect2 get_item_rect(TreeItem *p_item, int p_column = -1) const; + Rect2 get_item_rect(TreeItem *p_item, int p_column = -1, int p_button = -1) const; bool edit_selected(); bool is_editing(); diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index d7c76aa070..ca2dad71af 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -60,7 +60,7 @@ int VideoStreamPlayer::_audio_mix_callback(void *p_udata, const float *p_data, i ERR_FAIL_NULL_V(p_udata, 0); ERR_FAIL_NULL_V(p_data, 0); - VideoStreamPlayer *vp = (VideoStreamPlayer *)p_udata; + VideoStreamPlayer *vp = static_cast<VideoStreamPlayer *>(p_udata); int todo = MIN(vp->resampler.get_writer_space(), p_frames); @@ -77,7 +77,7 @@ int VideoStreamPlayer::_audio_mix_callback(void *p_udata, const float *p_data, i void VideoStreamPlayer::_mix_audios(void *p_self) { ERR_FAIL_NULL(p_self); - reinterpret_cast<VideoStreamPlayer *>(p_self)->_mix_audio(); + static_cast<VideoStreamPlayer *>(p_self)->_mix_audio(); } // Called from audio thread diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 15d36d8230..20f3f82a4e 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -463,10 +463,10 @@ void CanvasItem::draw_dashed_line(const Point2 &p_from, const Point2 &p_to, cons RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, off, p_to, p_color, p_width); } -void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width) { +void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, bool p_antialiased) { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width); + RenderingServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased); } void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width, bool p_antialiased) { @@ -883,10 +883,7 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_behind_parent", "enable"), &CanvasItem::set_draw_behind_parent); ClassDB::bind_method(D_METHOD("is_draw_behind_parent_enabled"), &CanvasItem::is_draw_behind_parent_enabled); - ClassDB::bind_method(D_METHOD("_set_on_top", "on_top"), &CanvasItem::_set_on_top); - ClassDB::bind_method(D_METHOD("_is_on_top"), &CanvasItem::_is_on_top); - - ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width"), &CanvasItem::draw_line, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash"), &CanvasItem::draw_dashed_line, DEFVAL(1.0), DEFVAL(2.0)); ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false)); @@ -959,7 +956,6 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_on_top", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_on_top", "_is_on_top"); //compatibility ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); @@ -1297,7 +1293,7 @@ void CanvasTexture::_bind_methods() { ADD_GROUP("Diffuse", "diffuse_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_diffuse_texture", "get_diffuse_texture"); - ADD_GROUP("Normalmap", "normal_"); + ADD_GROUP("NormalMap", "normal_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_normal_texture", "get_normal_texture"); ADD_GROUP("Specular", "specular_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "specular_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_specular_texture", "get_specular_texture"); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index dbc833aa5b..ad64f1ab5e 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -120,9 +120,6 @@ private: void _notify_transform(CanvasItem *p_node); - void _set_on_top(bool p_on_top) { set_draw_behind_parent(!p_on_top); } - bool _is_on_top() const { return !is_draw_behind_parent_enabled(); } - static CanvasItem *current_item_drawn; friend class Viewport; void _update_texture_repeat_changed(bool p_propagate); @@ -218,7 +215,7 @@ public: /* DRAWING API */ void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0); - void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0); + void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = 1.0, bool p_antialiased = false); void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = 1.0, bool p_antialiased = false); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index 7aa4d391f8..da96246de2 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -96,7 +96,7 @@ void CanvasLayer::_update_xform() { } void CanvasLayer::_update_locrotscale() { - ofs = transform.elements[2]; + ofs = transform.columns[2]; rot = transform.get_rotation(); scale = transform.get_scale(); locrotscale_dirty = false; diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index ac10c2bad8..34b0e19d31 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -164,7 +164,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust } void HTTPRequest::_thread_func(void *p_userdata) { - HTTPRequest *hr = (HTTPRequest *)p_userdata; + HTTPRequest *hr = static_cast<HTTPRequest *>(p_userdata); Error err = hr->_request(); @@ -197,10 +197,7 @@ void HTTPRequest::cancel_request() { thread.wait_to_finish(); } - if (file) { - memdelete(file); - file = nullptr; - } + file.unref(); client->close(); body.clear(); got_response = false; @@ -365,7 +362,7 @@ bool HTTPRequest::_update_connection() { if (!download_to_file.is_empty()) { file = FileAccess::open(download_to_file, FileAccess::WRITE); - if (!file) { + if (file.is_null()) { call_deferred(SNAME("_request_done"), RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray()); return true; } @@ -381,7 +378,7 @@ bool HTTPRequest::_update_connection() { if (chunk.size()) { downloaded.add(chunk.size()); - if (file) { + if (file.is_valid()) { const uint8_t *r = chunk.ptr(); file->store_buffer(r, chunk.size()); if (file->get_error() != OK) { @@ -642,9 +639,3 @@ HTTPRequest::HTTPRequest() { timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout)); add_child(timer); } - -HTTPRequest::~HTTPRequest() { - if (file) { - memdelete(file); - } -} diff --git a/scene/main/http_request.h b/scene/main/http_request.h index 26d648458f..49b4b1b30c 100644 --- a/scene/main/http_request.h +++ b/scene/main/http_request.h @@ -84,7 +84,7 @@ private: String download_to_file; - FileAccess *file = nullptr; + Ref<FileAccess> file; int body_len = -1; SafeNumeric<int> downloaded; @@ -144,7 +144,7 @@ public: void set_max_redirects(int p_max); int get_max_redirects() const; - Timer *timer; + Timer *timer = nullptr; void set_timeout(double p_timeout); double get_timeout(); @@ -158,7 +158,6 @@ public: void set_https_proxy(const String &p_host, int p_port); HTTPRequest(); - ~HTTPRequest(); }; VARIANT_ENUM_CAST(HTTPRequest::Result); diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp new file mode 100644 index 0000000000..395fdad9e4 --- /dev/null +++ b/scene/main/missing_node.cpp @@ -0,0 +1,98 @@ +/*************************************************************************/ +/* missing_node.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "missing_node.h" + +bool MissingNode::_set(const StringName &p_name, const Variant &p_value) { + if (is_recording_properties()) { + properties.insert(p_name, p_value); + return true; //always valid to set (add) + } else { + if (!properties.has(p_name)) { + return false; + } + + properties[p_name] = p_value; + return true; + } +} + +bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const { + if (!properties.has(p_name)) { + return false; + } + r_ret = properties[p_name]; + return true; +} + +void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const { + for (const KeyValue<StringName, Variant> &E : properties) { + p_list->push_back(PropertyInfo(E.value.get_type(), E.key)); + } +} + +void MissingNode::set_original_class(const String &p_class) { + original_class = p_class; +} + +String MissingNode::get_original_class() const { + return original_class; +} + +void MissingNode::set_recording_properties(bool p_enable) { + recording_properties = p_enable; +} + +bool MissingNode::is_recording_properties() const { + return recording_properties; +} + +TypedArray<String> MissingNode::get_configuration_warnings() const { + // The mere existence of this node is warning. + TypedArray<String> ret; + ret.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class)); + ret.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss.")); + return ret; +} + +void MissingNode::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class); + ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class); + + ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties); + ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties); + + // Expose, but not save. + ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties"); +} + +MissingNode::MissingNode() { +} diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h new file mode 100644 index 0000000000..d200fbb47f --- /dev/null +++ b/scene/main/missing_node.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* missing_node.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef MISSING_NODE_H +#define MISSING_NODE_H + +#include "core/io/missing_resource.h" +#include "scene/main/node.h" + +class MissingNode : public Node { + GDCLASS(MissingNode, Node) + HashMap<StringName, Variant> properties; + + String original_class; + bool recording_properties = false; + +protected: + 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; + + static void _bind_methods(); + +public: + void set_original_class(const String &p_class); + String get_original_class() const; + + void set_recording_properties(bool p_enable); + bool is_recording_properties() const; + + virtual TypedArray<String> get_configuration_warnings() const override; + + MissingNode(); +}; + +#endif // MISSING_NODE_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 7933045c5f..0c1a62c667 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -79,6 +79,9 @@ void Node::_notification(int p_notification) { if (data.input) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); } + if (data.shortcut_input) { + add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } if (data.unhandled_input) { add_to_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); } @@ -100,6 +103,9 @@ void Node::_notification(int p_notification) { if (data.input) { remove_from_group("_vp_input" + itos(get_viewport()->get_instance_id())); } + if (data.shortcut_input) { + remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } if (data.unhandled_input) { remove_from_group("_vp_unhandled_input" + itos(get_viewport()->get_instance_id())); } @@ -126,6 +132,10 @@ void Node::_notification(int p_notification) { set_process_input(true); } + if (GDVIRTUAL_IS_OVERRIDDEN(_shortcut_input)) { + set_process_shortcut_input(true); + } + if (GDVIRTUAL_IS_OVERRIDDEN(_unhandled_input)) { set_process_unhandled_input(true); } @@ -247,6 +257,9 @@ void Node::_propagate_after_exit_tree() { } if (!found) { + if (data.unique_name_in_owner) { + _release_unique_name_in_owner(); + } data.owner->data.owned.erase(data.OW); data.owner = nullptr; } @@ -640,21 +653,10 @@ void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg } Ref<MultiplayerAPI> Node::get_multiplayer() const { - if (multiplayer.is_valid()) { - return multiplayer; - } if (!is_inside_tree()) { return Ref<MultiplayerAPI>(); } - return get_tree()->get_multiplayer(); -} - -Ref<MultiplayerAPI> Node::get_custom_multiplayer() const { - return multiplayer; -} - -void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { - multiplayer = p_multiplayer; + return get_tree()->get_multiplayer(get_path()); } Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const { @@ -835,6 +837,26 @@ bool Node::is_processing_input() const { return data.input; } +void Node::set_process_shortcut_input(bool p_enable) { + if (p_enable == data.shortcut_input) { + return; + } + data.shortcut_input = p_enable; + if (!is_inside_tree()) { + return; + } + + if (p_enable) { + add_to_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } else { + remove_from_group("_vp_shortcut_input" + itos(get_viewport()->get_instance_id())); + } +} + +bool Node::is_processing_shortcut_input() const { + return data.shortcut_input; +} + void Node::set_process_unhandled_input(bool p_enable) { if (p_enable == data.unhandled_input) { return; @@ -887,12 +909,20 @@ void Node::set_name(const String &p_name) { String name = p_name.validate_node_name(); ERR_FAIL_COND(name.is_empty()); + + if (data.unique_name_in_owner && data.owner) { + _release_unique_name_in_owner(); + } data.name = name; if (data.parent) { data.parent->_validate_child_name(this, true); } + if (data.unique_name_in_owner && data.owner) { + _acquire_unique_name_in_owner(); + } + propagate_notification(NOTIFICATION_PATH_RENAMED); if (is_inside_tree()) { @@ -1273,6 +1303,24 @@ Node *Node::get_node_or_null(const NodePath &p_path) const { next = root; } + } else if (name.is_node_unique_name()) { + if (current->data.owned_unique_nodes.size()) { + // Has unique nodes in ownership + Node **unique = current->data.owned_unique_nodes.getptr(name); + if (!unique) { + return nullptr; + } + next = *unique; + } else if (current->data.owner) { + Node **unique = current->data.owner->data.owned_unique_nodes.getptr(name); + if (!unique) { + return nullptr; + } + next = *unique; + } else { + return nullptr; + } + } else { next = nullptr; @@ -1314,9 +1362,39 @@ bool Node::has_node(const NodePath &p_path) const { return get_node_or_null(p_path) != nullptr; } -TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bool p_recursive, bool p_owned) const { +// Finds the first child node (in tree order) whose name matches the given pattern. +// Can be recursive or not, and limited to owned nodes. +Node *Node::find_child(const String &p_pattern, bool p_recursive, bool p_owned) const { + ERR_FAIL_COND_V(p_pattern.is_empty(), nullptr); + + Node *const *cptr = data.children.ptr(); + int ccount = data.children.size(); + for (int i = 0; i < ccount; i++) { + if (p_owned && !cptr[i]->data.owner) { + continue; + } + if (cptr[i]->data.name.operator String().match(p_pattern)) { + return cptr[i]; + } + + if (!p_recursive) { + continue; + } + + Node *ret = cptr[i]->find_child(p_pattern, true, p_owned); + if (ret) { + return ret; + } + } + return nullptr; +} + +// Finds child nodes based on their name using pattern matching, or class name, +// or both (either pattern or type can be left empty). +// Can be recursive or not, and limited to owned nodes. +TypedArray<Node> Node::find_children(const String &p_pattern, const String &p_type, bool p_recursive, bool p_owned) const { TypedArray<Node> ret; - ERR_FAIL_COND_V(p_mask.is_empty() && p_type.is_empty(), ret); + ERR_FAIL_COND_V(p_pattern.is_empty() && p_type.is_empty(), ret); Node *const *cptr = data.children.ptr(); int ccount = data.children.size(); @@ -1325,8 +1403,8 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo continue; } - if (!p_mask.is_empty()) { - if (!cptr[i]->data.name.operator String().match(p_mask)) { + if (!p_pattern.is_empty()) { + if (!cptr[i]->data.name.operator String().match(p_pattern)) { continue; } else if (p_type.is_empty()) { ret.append(cptr[i]); @@ -1348,7 +1426,7 @@ TypedArray<Node> Node::find_nodes(const String &p_mask, const String &p_type, bo } if (p_recursive) { - ret.append_array(cptr[i]->find_nodes(p_mask, p_type, true, p_owned)); + ret.append_array(cptr[i]->find_children(p_pattern, p_type, true, p_owned)); } } @@ -1359,10 +1437,10 @@ Node *Node::get_parent() const { return data.parent; } -Node *Node::find_parent(const String &p_mask) const { +Node *Node::find_parent(const String &p_pattern) const { Node *p = data.parent; while (p) { - if (p->data.name.operator String().match(p_mask)) { + if (p->data.name.operator String().match(p_pattern)) { return p; } p = p->data.parent; @@ -1468,8 +1546,56 @@ void Node::_set_owner_nocheck(Node *p_owner) { data.OW = data.owner->data.owned.back(); } +void Node::_release_unique_name_in_owner() { + ERR_FAIL_NULL(data.owner); // Sanity check. + StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String()); + Node **which = data.owner->data.owned_unique_nodes.getptr(key); + if (which == nullptr || *which != this) { + return; // Ignore. + } + data.owner->data.owned_unique_nodes.erase(key); +} + +void Node::_acquire_unique_name_in_owner() { + ERR_FAIL_NULL(data.owner); // Sanity check. + StringName key = StringName(UNIQUE_NODE_PREFIX + data.name.operator String()); + Node **which = data.owner->data.owned_unique_nodes.getptr(key); + if (which != nullptr && *which != this) { + String which_path = is_inside_tree() ? (*which)->get_path() : data.owner->get_path_to(*which); + WARN_PRINT(vformat(RTR("Setting node name '%s' to be unique within scene for '%s', but it's already claimed by '%s'.\n'%s' is no longer set as having a unique name."), + get_name(), is_inside_tree() ? get_path() : data.owner->get_path_to(this), which_path, which_path)); + data.unique_name_in_owner = false; + return; + } + data.owner->data.owned_unique_nodes[key] = this; +} + +void Node::set_unique_name_in_owner(bool p_enabled) { + if (data.unique_name_in_owner == p_enabled) { + return; + } + + if (data.unique_name_in_owner && data.owner != nullptr) { + _release_unique_name_in_owner(); + } + data.unique_name_in_owner = p_enabled; + + if (data.unique_name_in_owner && data.owner != nullptr) { + _acquire_unique_name_in_owner(); + } + + update_configuration_warnings(); +} + +bool Node::is_unique_name_in_owner() const { + return data.unique_name_in_owner; +} + void Node::set_owner(Node *p_owner) { if (data.owner) { + if (data.unique_name_in_owner) { + _release_unique_name_in_owner(); + } data.owner->data.owned.erase(data.OW); data.OW = nullptr; data.owner = nullptr; @@ -1496,6 +1622,10 @@ void Node::set_owner(Node *p_owner) { ERR_FAIL_COND(!owner_valid); _set_owner_nocheck(p_owner); + + if (data.unique_name_in_owner) { + _acquire_unique_name_in_owner(); + } } Node *Node::get_owner() const { @@ -1507,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; @@ -1539,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; @@ -1633,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 { @@ -1882,35 +2012,28 @@ Node *Node::get_deepest_editable_node(Node *p_start_node) const { #ifdef TOOLS_ENABLED void Node::set_property_pinned(const String &p_property, bool p_pinned) { bool current_pinned = false; - bool has_pinned = has_meta("_edit_pinned_properties_"); - Array pinned; - String psa = get_property_store_alias(p_property); - if (has_pinned) { - pinned = get_meta("_edit_pinned_properties_"); - current_pinned = pinned.has(psa); - } + Array pinned = get_meta("_edit_pinned_properties_", Array()); + StringName psa = get_property_store_alias(p_property); + current_pinned = pinned.has(psa); if (current_pinned != p_pinned) { if (p_pinned) { pinned.append(psa); - if (!has_pinned) { - set_meta("_edit_pinned_properties_", pinned); - } } else { pinned.erase(psa); - if (pinned.is_empty()) { - remove_meta("_edit_pinned_properties_"); - } } } + + if (pinned.is_empty()) { + remove_meta("_edit_pinned_properties_"); + } else { + set_meta("_edit_pinned_properties_", pinned); + } } bool Node::is_property_pinned(const StringName &p_property) const { - if (!has_meta("_edit_pinned_properties_")) { - return false; - } - Array pinned = get_meta("_edit_pinned_properties_"); - String psa = get_property_store_alias(p_property); + Array pinned = get_meta("_edit_pinned_properties_", Array()); + StringName psa = get_property_store_alias(p_property); return pinned.has(psa); } @@ -1919,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()) { @@ -1965,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; @@ -2157,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<RES, RES>()); +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<RES, RES> &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. @@ -2177,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<RES, RES> &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); @@ -2188,7 +2311,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re Variant v = p_node->get(E.name); if (v.is_ref_counted()) { - RES res = v; + Ref<Resource> res = v; if (res.is_valid()) { if (p_resource_remap.has(res)) { p_node->set(E.name, p_resource_remap[res]); @@ -2203,7 +2326,7 @@ void Node::remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_re } } -void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &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); @@ -2214,7 +2337,7 @@ void Node::remap_nested_resources(RES p_resource, const Map<RES, RES> &p_resourc Variant v = p_resource->get(E.name); if (v.is_ref_counted()) { - RES res = v; + Ref<Resource> res = v; if (res.is_valid()) { if (p_resource_remap.has(res)) { p_resource->set(E.name, p_resource_remap[res]); @@ -2363,7 +2486,7 @@ bool Node::has_node_and_resource(const NodePath &p_path) const { if (!has_node(p_path)) { return false; } - RES res; + Ref<Resource> res; Vector<StringName> leftover_path; Node *node = get_node_and_resource(p_path, res, leftover_path, false); @@ -2371,7 +2494,7 @@ bool Node::has_node_and_resource(const NodePath &p_path) const { } Array Node::_get_node_and_resource(const NodePath &p_path) { - RES res; + Ref<Resource> res; Vector<StringName> leftover_path; Node *node = get_node_and_resource(p_path, res, leftover_path, false); Array result; @@ -2393,9 +2516,9 @@ Array Node::_get_node_and_resource(const NodePath &p_path) { return result; } -Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const { +Node *Node::get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const { Node *node = get_node(p_path); - r_res = RES(); + r_res = Ref<Resource>(); r_leftover_subpath = Vector<StringName>(); if (!node) { return nullptr; @@ -2411,7 +2534,7 @@ Node *Node::get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<Str return nullptr; } - RES new_res = new_res_v; + Ref<Resource> new_res = new_res_v; if (new_res.is_null()) { // No longer a resource, assume property break; @@ -2555,16 +2678,16 @@ void Node::clear_internal_tree_resource_paths() { } TypedArray<String> Node::get_configuration_warnings() const { + TypedArray<String> ret; + Vector<String> warnings; if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) { - TypedArray<String> ret; - ret.resize(warnings.size()); for (int i = 0; i < warnings.size(); i++) { - ret[i] = warnings[i]; + ret.push_back(warnings[i]); } - return ret; } - return Array(); + + return ret; } String Node::get_configuration_warnings_as_string() const { @@ -2615,6 +2738,15 @@ void Node::_call_input(const Ref<InputEvent> &p_event) { } input(p_event); } + +void Node::_call_shortcut_input(const Ref<InputEvent> &p_event) { + GDVIRTUAL_CALL(_shortcut_input, p_event); + if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { + return; + } + shortcut_input(p_event); +} + void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) { GDVIRTUAL_CALL(_unhandled_input, p_event); if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { @@ -2622,6 +2754,7 @@ void Node::_call_unhandled_input(const Ref<InputEvent> &p_event) { } unhandled_input(p_event); } + void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) { GDVIRTUAL_CALL(_unhandled_key_input, p_event); if (!is_inside_tree() || !get_viewport() || get_viewport()->is_input_handled()) { @@ -2633,6 +2766,9 @@ void Node::_call_unhandled_key_input(const Ref<InputEvent> &p_event) { void Node::input(const Ref<InputEvent> &p_event) { } +void Node::shortcut_input(const Ref<InputEvent> &p_key_event) { +} + void Node::unhandled_input(const Ref<InputEvent> &p_event) { } @@ -2658,8 +2794,9 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node", "path"), &Node::get_node); ClassDB::bind_method(D_METHOD("get_node_or_null", "path"), &Node::get_node_or_null); ClassDB::bind_method(D_METHOD("get_parent"), &Node::get_parent); - ClassDB::bind_method(D_METHOD("find_nodes", "mask", "type", "recursive", "owned"), &Node::find_nodes, DEFVAL(""), DEFVAL(true), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("find_parent", "mask"), &Node::find_parent); + ClassDB::bind_method(D_METHOD("find_child", "pattern", "recursive", "owned"), &Node::find_child, DEFVAL(true), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("find_children", "pattern", "type", "recursive", "owned"), &Node::find_children, DEFVAL(""), DEFVAL(true), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("find_parent", "pattern"), &Node::find_parent); ClassDB::bind_method(D_METHOD("has_node_and_resource", "path"), &Node::has_node_and_resource); ClassDB::bind_method(D_METHOD("get_node_and_resource", "path"), &Node::_get_node_and_resource); @@ -2694,6 +2831,8 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing); ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input); ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input); + ClassDB::bind_method(D_METHOD("set_process_shortcut_input", "enable"), &Node::set_process_shortcut_input); + ClassDB::bind_method(D_METHOD("is_processing_shortcut_input"), &Node::is_processing_shortcut_input); ClassDB::bind_method(D_METHOD("set_process_unhandled_input", "enable"), &Node::set_process_unhandled_input); ClassDB::bind_method(D_METHOD("is_processing_unhandled_input"), &Node::is_processing_unhandled_input); ClassDB::bind_method(D_METHOD("set_process_unhandled_key_input", "enable"), &Node::set_process_unhandled_key_input); @@ -2735,8 +2874,6 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority); ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer); - ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer); - ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer); ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description); @@ -2745,6 +2882,9 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); + ClassDB::bind_method(D_METHOD("set_unique_name_in_owner", "enable"), &Node::set_unique_name_in_owner); + ClassDB::bind_method(D_METHOD("is_unique_name_in_owner"), &Node::is_unique_name_in_owner); + #ifdef TOOLS_ENABLED ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned); #endif @@ -2835,10 +2975,10 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("child_exited_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node"))); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "unique_name_in_owner", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_unique_name_in_owner", "is_unique_name_in_owner"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer"); ADD_GROUP("Process", "process_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode"); @@ -2854,6 +2994,7 @@ void Node::_bind_methods() { GDVIRTUAL_BIND(_ready); GDVIRTUAL_BIND(_get_configuration_warnings); GDVIRTUAL_BIND(_input, "event"); + GDVIRTUAL_BIND(_shortcut_input, "event"); GDVIRTUAL_BIND(_unhandled_input, "event"); GDVIRTUAL_BIND(_unhandled_key_input, "event"); } diff --git a/scene/main/node.h b/scene/main/node.h index 1f973a0cb4..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" @@ -99,6 +99,9 @@ private: Node *parent = nullptr; Node *owner = nullptr; Vector<Node *> children; + HashMap<StringName, Node *> owned_unique_nodes; + bool unique_name_in_owner = false; + int internal_children_front = 0; int internal_children_back = 0; int pos = -1; @@ -116,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; @@ -136,6 +139,7 @@ private: bool process_internal = false; bool input = false; + bool shortcut_input = false; bool unhandled_input = false; bool unhandled_key_input = false; @@ -173,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; @@ -192,6 +196,9 @@ private: _FORCE_INLINE_ bool _can_process(bool p_paused) const; _FORCE_INLINE_ bool _is_enabled() const; + void _release_unique_name_in_owner(); + void _acquire_unique_name_in_owner(); + protected: void _block() { data.blocked++; } void _unblock() { data.blocked--; } @@ -215,11 +222,13 @@ protected: //call from SceneTree void _call_input(const Ref<InputEvent> &p_event); + void _call_shortcut_input(const Ref<InputEvent> &p_event); void _call_unhandled_input(const Ref<InputEvent> &p_event); void _call_unhandled_key_input(const Ref<InputEvent> &p_event); protected: virtual void input(const Ref<InputEvent> &p_event); + virtual void shortcut_input(const Ref<InputEvent> &p_key_event); virtual void unhandled_input(const Ref<InputEvent> &p_event); virtual void unhandled_key_input(const Ref<InputEvent> &p_key_event); @@ -231,6 +240,7 @@ protected: GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings) GDVIRTUAL1(_input, Ref<InputEvent>) + GDVIRTUAL1(_shortcut_input, Ref<InputEvent>) GDVIRTUAL1(_unhandled_input, Ref<InputEvent>) GDVIRTUAL1(_unhandled_key_input, Ref<InputEvent>) @@ -300,12 +310,13 @@ public: bool has_node(const NodePath &p_path) const; Node *get_node(const NodePath &p_path) const; Node *get_node_or_null(const NodePath &p_path) const; - TypedArray<Node> find_nodes(const String &p_mask, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const; + Node *find_child(const String &p_pattern, bool p_recursive = true, bool p_owned = true) const; + TypedArray<Node> find_children(const String &p_pattern, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const; bool has_node_and_resource(const NodePath &p_path) const; - Node *get_node_and_resource(const NodePath &p_path, RES &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; + Node *get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const; Node *get_parent() const; - Node *find_parent(const String &p_mask) const; + Node *find_parent(const String &p_pattern) const; _FORCE_INLINE_ SceneTree *get_tree() const { ERR_FAIL_COND_V(!data.tree, nullptr); @@ -341,6 +352,9 @@ public: Node *get_owner() const; void get_owned_by(Node *p_by, List<Node *> *p_owned); + void set_unique_name_in_owner(bool p_enabled); + bool is_unique_name_in_owner() const; + void remove_and_skip(); int get_index(bool p_include_internal = true) const; @@ -364,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; @@ -395,6 +409,9 @@ public: void set_process_input(bool p_enable); bool is_processing_input() const; + void set_process_shortcut_input(bool p_enable); + bool is_processing_shortcut_input() const; + void set_process_unhandled_input(bool p_enable); bool is_processing_unhandled_input() const; @@ -403,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<RES, RES> &p_resource_remap) const; - void remap_node_resources(Node *p_node, const Map<RES, RES> &p_resource_remap) const; - void remap_nested_resources(RES p_resource, const Map<RES, RES> &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 @@ -498,8 +515,6 @@ public: void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount); Ref<MultiplayerAPI> get_multiplayer() const; - Ref<MultiplayerAPI> get_custom_multiplayer() const; - void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer); Node(); ~Node(); @@ -507,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 49010095ff..b3595c6227 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -41,7 +41,7 @@ void ResourcePreloader::_set_resources(const Array &p_data) { for (int i = 0; i < resdata.size(); i++) { String name = names[i]; - RES resource = resdata[i]; + Ref<Resource> resource = resdata[i]; ERR_CONTINUE(!resource.is_valid()); resources[name] = resource; @@ -55,14 +55,14 @@ 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, RES> &E : resources) { + 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()) { + for (RBSet<String>::Element *E = sorted_names.front(); E; E = E->next()) { names.set(i, E->get()); arr[i] = resources[E->get()]; i++; @@ -74,7 +74,7 @@ Array ResourcePreloader::_get_resources() const { return res; } -void ResourcePreloader::add_resource(const StringName &p_name, const RES &p_resource) { +void ResourcePreloader::add_resource(const StringName &p_name, const Ref<Resource> &p_resource) { ERR_FAIL_COND(p_resource.is_null()); if (resources.has(p_name)) { StringName new_name; @@ -104,7 +104,7 @@ void ResourcePreloader::remove_resource(const StringName &p_name) { void ResourcePreloader::rename_resource(const StringName &p_from_name, const StringName &p_to_name) { ERR_FAIL_COND(!resources.has(p_from_name)); - RES res = resources[p_from_name]; + Ref<Resource> res = resources[p_from_name]; resources.erase(p_from_name); add_resource(p_to_name, res); @@ -114,8 +114,8 @@ bool ResourcePreloader::has_resource(const StringName &p_name) const { return resources.has(p_name); } -RES ResourcePreloader::get_resource(const StringName &p_name) const { - ERR_FAIL_COND_V(!resources.has(p_name), RES()); +Ref<Resource> ResourcePreloader::get_resource(const StringName &p_name) const { + ERR_FAIL_COND_V(!resources.has(p_name), Ref<Resource>()); return resources[p_name]; } @@ -123,15 +123,16 @@ Vector<String> ResourcePreloader::_get_resource_list() const { Vector<String> res; res.resize(resources.size()); int i = 0; - for (Map<StringName, RES>::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; } void ResourcePreloader::get_resource_list(List<StringName> *p_list) { - for (const KeyValue<StringName, RES> &E : resources) { + for (const KeyValue<StringName, Ref<Resource>> &E : resources) { p_list->push_back(E.key); } } diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h index aabb109d56..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, RES> resources; + HashMap<StringName, Ref<Resource>> resources; void _set_resources(const Array &p_data); Array _get_resources() const; @@ -46,11 +46,11 @@ protected: static void _bind_methods(); public: - void add_resource(const StringName &p_name, const RES &p_resource); + void add_resource(const StringName &p_name, const Ref<Resource> &p_resource); void remove_resource(const StringName &p_name); void rename_resource(const StringName &p_from_name, const StringName &p_to_name); bool has_resource(const StringName &p_name) const; - RES get_resource(const StringName &p_name) const; + Ref<Resource> get_resource(const StringName &p_name) const; void get_resource_list(List<StringName> *p_list); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 8eeedf51a9..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_REALTIME, 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,16 +211,16 @@ 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; } - if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) { + if (p_call_flags & GROUP_CALL_UNIQUE && p_call_flags & GROUP_CALL_DEFERRED) { ERR_FAIL_COND(ugc_locked); UGCall ug; @@ -254,7 +254,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { @@ -268,7 +268,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { Callable::CallError ce; nodes[i]->callp(p_function, p_args, p_argcount, ce); } else { @@ -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; } @@ -307,7 +307,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->notification(p_notification); } else { MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); @@ -320,7 +320,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->notification(p_notification); } else { MessageQueue::get_singleton()->push_notification(nodes[i], p_notification); @@ -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; } @@ -358,7 +358,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->set(p_name, p_value); } else { MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); @@ -371,7 +371,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group continue; } - if (p_call_flags & GROUP_CALL_REALTIME) { + if (!(p_call_flags & GROUP_CALL_DEFERRED)) { nodes[i]->set(p_name, p_value); } else { MessageQueue::get_singleton()->push_set(nodes[i], p_name, p_value); @@ -390,7 +390,7 @@ void SceneTree::notify_group(const StringName &p_group, int p_notification) { } void SceneTree::set_group(const StringName &p_group, const String &p_name, const Variant &p_value) { - set_group_flags(0, p_group, p_name, p_value); + set_group_flags(GROUP_CALL_DEFAULT, p_group, p_name, p_value); } void SceneTree::initialize() { @@ -413,7 +413,7 @@ bool SceneTree::physics_process(double p_time) { emit_signal(SNAME("physics_frame")); _notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - call_group_flags(GROUP_CALL_REALTIME, SNAME("_picking_viewports"), SNAME("_process_picking")); + call_group(SNAME("_picking_viewports"), SNAME("_process_picking")); _notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS); _flush_ugc(); MessageQueue::get_singleton()->flush(); //small little hack @@ -438,6 +438,9 @@ bool SceneTree::process(double p_time) { if (multiplayer_poll) { multiplayer->poll(); + for (KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) { + E.value->poll(); + } } emit_signal(SNAME("process_frame")); @@ -815,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; } @@ -859,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; } @@ -897,6 +900,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal case CALL_INPUT_TYPE_INPUT: n->_call_input(p_input); break; + case CALL_INPUT_TYPE_SHORTCUT_INPUT: + n->_call_shortcut_input(p_input); + break; case CALL_INPUT_TYPE_UNHANDLED_INPUT: n->_call_unhandled_input(p_input); break; @@ -937,7 +943,7 @@ void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::Ca StringName group = *p_args[0]; StringName method = *p_args[1]; - call_group_flagsp(0, group, method, p_args + 2, p_argcount - 2); + call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2); } int64_t SceneTree::get_frame() const { @@ -946,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]; } @@ -972,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 + 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.size() == 0) { + 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]); } @@ -1110,9 +1116,7 @@ Ref<SceneTreeTimer> SceneTree::create_timer(double p_delay_sec, bool p_process_a } Ref<Tween> SceneTree::create_tween() { - Ref<Tween> tween; - tween.instantiate(); - tween->set_valid(true); + Ref<Tween> tween = memnew(Tween(true)); tweens.push_back(tween); return tween; } @@ -1130,8 +1134,50 @@ Array SceneTree::get_processed_tweens() { return ret; } -Ref<MultiplayerAPI> SceneTree::get_multiplayer() const { - return multiplayer; +Ref<MultiplayerAPI> SceneTree::get_multiplayer(const NodePath &p_for_path) const { + Ref<MultiplayerAPI> out = multiplayer; + for (const KeyValue<NodePath, Ref<MultiplayerAPI>> &E : custom_multiplayers) { + const Vector<StringName> snames = E.key.get_names(); + const Vector<StringName> tnames = p_for_path.get_names(); + if (tnames.size() < snames.size()) { + continue; + } + const StringName *sptr = snames.ptr(); + const StringName *nptr = tnames.ptr(); + bool valid = true; + for (int i = 0; i < snames.size(); i++) { + if (sptr[i] != nptr[i]) { + valid = false; + break; + } + } + if (valid) { + out = E.value; + break; + } + } + return out; +} + +void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path) { + if (p_root_path.is_empty()) { + ERR_FAIL_COND(!p_multiplayer.is_valid()); + if (multiplayer.is_valid()) { + multiplayer->set_root_path(NodePath()); + } + multiplayer = p_multiplayer; + multiplayer->set_root_path("/" + root->get_name()); + } else { + if (p_multiplayer.is_valid()) { + custom_multiplayers[p_root_path] = p_multiplayer; + p_multiplayer->set_root_path(p_root_path); + } else { + if (custom_multiplayers.has(p_root_path)) { + custom_multiplayers[p_root_path]->set_root_path(NodePath()); + custom_multiplayers.erase(p_root_path); + } + } + } } void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) { @@ -1142,13 +1188,6 @@ bool SceneTree::is_multiplayer_poll_enabled() const { return multiplayer_poll; } -void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) { - ERR_FAIL_COND(!p_multiplayer.is_valid()); - - multiplayer = p_multiplayer; - multiplayer->set_root_path("/" + root->get_name()); -} - void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root"), &SceneTree::get_root); ClassDB::bind_method(D_METHOD("has_group", "name"), &SceneTree::has_group); @@ -1211,8 +1250,8 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene); - ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer); - ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer); + ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer", "root_path"), &SceneTree::set_multiplayer, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("get_multiplayer", "for_path"), &SceneTree::get_multiplayer, DEFVAL(NodePath())); ClassDB::bind_method(D_METHOD("set_multiplayer_poll_enabled", "enabled"), &SceneTree::set_multiplayer_poll_enabled); ClassDB::bind_method(D_METHOD("is_multiplayer_poll_enabled"), &SceneTree::is_multiplayer_poll_enabled); @@ -1222,7 +1261,6 @@ void SceneTree::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_edited_scene_root", "get_edited_scene_root"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_current_scene", "get_current_scene"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_root"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_multiplayer", "get_multiplayer"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multiplayer_poll"), "set_multiplayer_poll_enabled", "is_multiplayer_poll_enabled"); ADD_SIGNAL(MethodInfo("tree_changed")); @@ -1237,7 +1275,7 @@ void SceneTree::_bind_methods() { BIND_ENUM_CONSTANT(GROUP_CALL_DEFAULT); BIND_ENUM_CONSTANT(GROUP_CALL_REVERSE); - BIND_ENUM_CONSTANT(GROUP_CALL_REALTIME); + BIND_ENUM_CONSTANT(GROUP_CALL_DEFERRED); BIND_ENUM_CONSTANT(GROUP_CALL_UNIQUE); } @@ -1259,7 +1297,7 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { if (p_function == "change_scene") { - DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); List<String> directories; directories.push_back(dir_access->get_current_dir()); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 10839ca425..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; @@ -115,22 +115,26 @@ private: int node_count = 0; #ifdef TOOLS_ENABLED - Node *edited_scene_root; + Node *edited_scene_root = nullptr; #endif struct UGCall { 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(); @@ -138,7 +142,7 @@ private: Array _get_nodes_in_group(const StringName &p_group); - Node *current_scene; + Node *current_scene = nullptr; Color debug_collisions_color; Color debug_collision_contact_color; @@ -151,7 +155,6 @@ private: int collision_debug_contacts; void _change_scene(Node *p_to); - //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2); List<Ref<SceneTreeTimer>> timers; List<Ref<Tween>> tweens; @@ -159,6 +162,7 @@ private: ///network/// Ref<MultiplayerAPI> multiplayer; + HashMap<NodePath, Ref<MultiplayerAPI>> custom_multiplayers; bool multiplayer_poll = true; static SceneTree *singleton; @@ -204,6 +208,7 @@ private: enum CallInputType { CALL_INPUT_TYPE_INPUT, + CALL_INPUT_TYPE_SHORTCUT_INPUT, CALL_INPUT_TYPE_UNHANDLED_INPUT, CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, }; @@ -223,7 +228,7 @@ public: enum GroupCallFlags { GROUP_CALL_DEFAULT = 0, GROUP_CALL_REVERSE = 1, - GROUP_CALL_REALTIME = 2, + GROUP_CALL_DEFERRED = 2, GROUP_CALL_UNIQUE = 4, }; @@ -233,17 +238,20 @@ public: void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification); void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value); + // `notify_group()` is immediate by default since Godot 4.0. void notify_group(const StringName &p_group, int p_notification); + // `set_group()` is immediate by default since Godot 4.0. void set_group(const StringName &p_group, const String &p_name, const Variant &p_value); template <typename... VarArgs> + // `call_group()` is immediate by default since Godot 4.0. void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; for (uint32_t i = 0; i < sizeof...(p_args); i++) { argptrs[i] = &args[i]; } - call_group_flagsp(0, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } template <typename... VarArgs> @@ -350,10 +358,10 @@ public: //network API - Ref<MultiplayerAPI> get_multiplayer() const; + Ref<MultiplayerAPI> get_multiplayer(const NodePath &p_for_path = NodePath()) const; + void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path = NodePath()); void set_multiplayer_poll_enabled(bool p_enabled); bool is_multiplayer_poll_enabled() const; - void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer); static void add_idle_callback(IdleCallback p_callback); diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp index 9b85e9db38..0049359cad 100644 --- a/scene/main/shader_globals_override.cpp +++ b/scene/main/shader_globals_override.cpp @@ -155,7 +155,7 @@ void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const pinfo.type = Variant::VECTOR3; } break; case RS::GLOBAL_VAR_TYPE_VEC4: { - pinfo.type = Variant::PLANE; + pinfo.type = Variant::QUATERNION; } break; case RS::GLOBAL_VAR_TYPE_RECT2: { pinfo.type = Variant::RECT2; @@ -229,20 +229,19 @@ void ShaderGlobalsOverride::_activate() { active = true; add_to_group(SceneStringNames::get_singleton()->shader_overrides_group_active); - const StringName *K = nullptr; - while ((K = overrides.next(K))) { - Override *o = overrides.getptr(*K); + for (const KeyValue<StringName, Override> &E : overrides) { + const Override *o = &E.value; if (o->in_use && o->override.get_type() != Variant::NIL) { if (o->override.get_type() == Variant::OBJECT) { RID tex_rid = o->override; - RS::get_singleton()->global_variable_set_override(*K, tex_rid); + RS::get_singleton()->global_variable_set_override(E.key, tex_rid); } else { - RS::get_singleton()->global_variable_set_override(*K, o->override); + RS::get_singleton()->global_variable_set_override(E.key, o->override); } } - } - update_configuration_warnings(); //may have activated + update_configuration_warnings(); //may have activated + } } } @@ -256,18 +255,17 @@ void ShaderGlobalsOverride::_notification(int p_what) { case Node3D::NOTIFICATION_EXIT_TREE: { if (active) { //remove overrides - const StringName *K = nullptr; - while ((K = overrides.next(K))) { - Override *o = overrides.getptr(*K); + for (const KeyValue<StringName, Override> &E : overrides) { + const Override *o = &E.value; if (o->in_use) { - RS::get_singleton()->global_variable_set_override(*K, Variant()); + RS::get_singleton()->global_variable_set_override(E.key, Variant()); } } } remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group_active); remove_from_group(SceneStringNames::get_singleton()->shader_overrides_group); - get_tree()->call_group(SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringNames::get_singleton()->shader_overrides_group, "_activate"); //another may want to activate when this is removed active = false; } break; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index a38bed71d1..a2399c8b5a 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -239,8 +239,8 @@ void Viewport::_sub_window_update(Window *p_window) { int font_size = p_window->get_theme_font_size(SNAME("title_font_size")); Color title_color = p_window->get_theme_color(SNAME("title_color")); int title_height = p_window->get_theme_constant(SNAME("title_height")); - int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_ofs")); - int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_ofs")); + int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_offset")); + int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_offset")); TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs); @@ -412,7 +412,7 @@ 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()) { + for (RBSet<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { if (first == nullptr || first->is_greater_than(E->get())) { first = E->get(); } @@ -426,7 +426,7 @@ 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()) { + for (RBSet<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) { if (first == nullptr || first->is_greater_than(E->get())) { first = E->get(); } @@ -647,7 +647,7 @@ 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 (RBSet<CanvasLayer *>::Element *E = canvas_layers.front(); E; E = E->next()) { Transform2D canvas_transform; ObjectID canvas_layer_id; if (E->get()) { @@ -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; } } @@ -1104,7 +1104,7 @@ Transform2D Viewport::_get_input_pre_xform() const { Transform2D pre_xf; if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) { - pre_xf.elements[2] = -to_screen_rect.position; + pre_xf.columns[2] = -to_screen_rect.position; pre_xf.scale(Vector2(size) / to_screen_rect.size); } @@ -2186,7 +2186,7 @@ void Viewport::_gui_control_grab_focus(Control *p_control) { // No need for change. return; } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, "_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window()); + get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window()); gui.key_focus = p_control; emit_signal(SNAME("gui_focus_changed"), p_control); p_control->notification(Control::NOTIFICATION_FOCUS_ENTER); @@ -2250,14 +2250,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 +2267,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 +2276,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()) { @@ -2583,8 +2583,8 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { if (title_bar.has_point(mb->get_position())) { click_on_window = true; - int close_h_ofs = sw.window->get_theme_constant(SNAME("close_h_ofs")); - int close_v_ofs = sw.window->get_theme_constant(SNAME("close_v_ofs")); + int close_h_ofs = sw.window->get_theme_constant(SNAME("close_h_offset")); + int close_v_ofs = sw.window->get_theme_constant(SNAME("close_v_offset")); Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close")); Rect2 close_rect; @@ -2744,11 +2744,18 @@ void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local ev = p_event; } + // Shortcut Input. + if (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr) { + get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, ev, this); + } + // Unhandled Input. - get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this); + if (!is_input_handled()) { + get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, ev, this); + } - // Unhandled key Input - used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, etc. - if (!is_input_handled() && (Object::cast_to<InputEventKey>(*ev) != nullptr || Object::cast_to<InputEventShortcut>(*ev) != nullptr)) { + // Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts. + if (!is_input_handled() && (Object::cast_to<InputEventKey>(*ev) != nullptr)) { get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, ev, this); } @@ -3172,7 +3179,7 @@ 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()) { + for (RBSet<AudioListener3D *>::Element *E = audio_listener_3d_set.front(); E; E = E->next()) { if (p_exclude == E->get()) { continue; } @@ -3260,7 +3267,7 @@ 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()) { + for (RBSet<Camera3D *>::Element *E = camera_3d_set.front(); E; E = E->next()) { if (p_exclude == E->get()) { continue; } @@ -3719,7 +3726,6 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d"); #endif // _3D_DISABLED @@ -3737,7 +3743,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); #ifndef _3D_DISABLED ADD_GROUP("Scaling 3D", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Disabled (Slowest),Bilinear (Fastest),FSR (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.1"), "set_fsr_mipmap_bias", "get_fsr_mipmap_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness"); @@ -3747,6 +3753,9 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat"); ADD_GROUP("Audio Listener", "audio_listener_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d"); +#ifndef _3D_DISABLED + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d"); +#endif ADD_GROUP("Physics", "physics_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking"); ADD_GROUP("GUI", "gui_"); @@ -3879,6 +3888,7 @@ Viewport::Viewport() { input_group = "_vp_input" + id; gui_input_group = "_vp_gui_input" + id; unhandled_input_group = "_vp_unhandled_input" + id; + shortcut_input_group = "_vp_shortcut_input" + id; unhandled_key_input_group = "_vp_unhandled_key_input" + id; // Window tooltip. @@ -3903,7 +3913,7 @@ Viewport::Viewport() { Viewport::~Viewport() { // Erase itself from viewport textures. - for (Set<ViewportTexture *>::Element *E = viewport_textures.front(); E; E = E->next()) { + for (RBSet<ViewportTexture *>::Element *E = viewport_textures.front(); E; E = E->next()) { E->get()->vp = nullptr; } RenderingServer::get_singleton()->free(viewport); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 80be53fab8..c1e4b30c20 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; @@ -230,7 +230,6 @@ private: Rect2 last_vp_rect; bool transparent_bg = false; - bool filter; bool gen_mipmaps = false; bool snap_controls_to_pixels = true; @@ -259,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); @@ -270,6 +269,7 @@ private: Rect2i to_screen_rect; StringName input_group; StringName gui_input_group; + StringName shortcut_input_group; StringName unhandled_input_group; StringName unhandled_key_input_group; @@ -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; @@ -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 2faa107fb4..d8264e7d33 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -263,7 +263,7 @@ 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()) { + for (RBSet<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); } @@ -290,7 +290,7 @@ 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()) { + for (RBSet<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); } @@ -981,7 +981,7 @@ void Window::_window_input_text(const String &p_text) { } void Window::_window_drop_files(const Vector<String> &p_files) { - emit_signal(SNAME("files_dropped"), p_files, current_screen); + emit_signal(SNAME("files_dropped"), p_files); } Viewport *Window::get_parent_viewport() const { @@ -1045,7 +1045,9 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio Rect2i popup_rect; popup_rect.size = Vector2i(MIN(size_ratio.x, p_size.x), MIN(size_ratio.y, p_size.y)); - popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; + if (parent_rect != Rect2()) { + popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; + } popup(popup_rect); } @@ -1066,12 +1068,13 @@ void Window::popup_centered(const Size2i &p_minsize) { } Rect2i popup_rect; - if (p_minsize == Size2i()) { - popup_rect.size = _get_contents_minimum_size(); - } else { - popup_rect.size = p_minsize; + Size2 contents_minsize = _get_contents_minimum_size(); + popup_rect.size.x = MAX(p_minsize.x, contents_minsize.x); + popup_rect.size.y = MAX(p_minsize.y, contents_minsize.y); + + if (parent_rect != Rect2()) { + popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; } - popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; popup(popup_rect); } @@ -1079,6 +1082,7 @@ void Window::popup_centered(const Size2i &p_minsize) { void Window::popup_centered_ratio(float p_ratio) { ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND_MSG(window_id == DisplayServer::MAIN_WINDOW_ID, "Can't popup the main window."); + ERR_FAIL_COND_MSG(p_ratio <= 0.0 || p_ratio > 1.0, "Ratio must be between 0.0 and 1.0!"); Rect2 parent_rect; @@ -1092,8 +1096,10 @@ void Window::popup_centered_ratio(float p_ratio) { } Rect2i popup_rect; - popup_rect.size = parent_rect.size * p_ratio; - popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; + if (parent_rect != Rect2()) { + popup_rect.size = parent_rect.size * p_ratio; + popup_rect.position = parent_rect.position + (parent_rect.size - popup_rect.size) / 2; + } popup(popup_rect); } 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.cpp b/scene/multiplayer/multiplayer_spawner.cpp index 25ab27f3e7..a9b9ffa989 100644 --- a/scene/multiplayer/multiplayer_spawner.cpp +++ b/scene/multiplayer/multiplayer_spawner.cpp @@ -91,9 +91,9 @@ void MultiplayerSpawner::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { _update_spawn_node(); - const ObjectID *oid = nullptr; - while ((oid = tracked_nodes.next(oid))) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid)); + + for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) { + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key)); ERR_CONTINUE(!node); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit)); // This is unlikely, but might still crash the engine. 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 f05dc5a2da..a7e84b6bca 100644 --- a/scene/multiplayer/scene_cache_interface.cpp +++ b/scene/multiplayer/scene_cache_interface.cpp @@ -50,10 +50,8 @@ void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) { path_get_cache.erase(p_id); // Cleanup sent cache. // Some refactoring is needed to make this faster and do paths GC. - List<NodePath> keys; - path_send_cache.get_key_list(&keys); - for (const NodePath &E : keys) { - PathSentCache *psc = path_send_cache.getptr(E); + for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) { + PathSentCache *psc = path_send_cache.getptr(E.key); psc->confirmed_peers.erase(p_id); } } @@ -134,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) { @@ -184,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) { @@ -207,16 +205,16 @@ 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()) { + for (const RBSet<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) { if (p_peer_id < 0 && E->get() == -p_peer_id) { continue; // Continue, excluded. } @@ -224,11 +222,11 @@ bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int 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->get()); if (!F) { peers_to_add.push_back(E->get()); // Need to also be notified. has_all_peers = false; - } else if (!F->get()) { + } else if (!F->value) { has_all_peers = false; } } @@ -244,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 c709d26b51..3116233b5b 100644 --- a/scene/multiplayer/scene_cache_interface.h +++ b/scene/multiplayer/scene_cache_interface.h @@ -37,11 +37,11 @@ class SceneCacheInterface : public MultiplayerCacheInterface { GDCLASS(SceneCacheInterface, MultiplayerCacheInterface); private: - MultiplayerAPI *multiplayer; + MultiplayerAPI *multiplayer = nullptr; //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 0764f136e4..55266c53ad 100644 --- a/scene/multiplayer/scene_replication_interface.cpp +++ b/scene/multiplayer/scene_replication_interface.cpp @@ -49,9 +49,8 @@ void SceneReplicationInterface::make_default() { void SceneReplicationInterface::_free_remotes(int p_id) { const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id); - const uint32_t *k = nullptr; - while ((k = remotes.next(k))) { - Node *node = rep_state->get_node(remotes.get(*k)); + for (const KeyValue<uint32_t, ObjectID> &E : remotes) { + Node *node = rep_state->get_node(E.value); ERR_CONTINUE(!node); node->queue_delete(); } @@ -309,12 +308,15 @@ Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p Error err = rep_state->peer_del_remote(p_from, net_id, &node); ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(!node, ERR_BUG); + if (node->get_parent() != nullptr) { + node->get_parent()->remove_child(node); + } node->queue_delete(); return OK; } 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_interface.h b/scene/multiplayer/scene_replication_interface.h index 855878d029..60ac95c93c 100644 --- a/scene/multiplayer/scene_replication_interface.h +++ b/scene/multiplayer/scene_replication_interface.h @@ -47,7 +47,7 @@ private: void _free_remotes(int p_peer); Ref<SceneReplicationState> rep_state; - MultiplayerAPI *multiplayer; + MultiplayerAPI *multiplayer = nullptr; PackedByteArray packet_cache; int sync_mtu = 1350; // Highly dependent on underlying protocol. diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp index b8dadeff24..e5cc57ff31 100644 --- a/scene/multiplayer/scene_replication_state.cpp +++ b/scene/multiplayer/scene_replication_state.cpp @@ -55,9 +55,8 @@ void SceneReplicationState::_untrack(const ObjectID &p_id) { } // If we spawned or synced it, we need to remove it from any peer it was sent to. if (net_id || peer == 0) { - const int *k = nullptr; - while ((k = peers_info.next(k))) { - peers_info.get(*k).known_nodes.erase(p_id); + for (KeyValue<int, PeerInfo> &E : peers_info) { + E.value.known_nodes.erase(p_id); } } } @@ -94,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; } @@ -134,9 +133,8 @@ void SceneReplicationState::reset() { peers_info.clear(); known_peers.clear(); // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned. - const ObjectID *oid = nullptr; - while ((oid = tracked_nodes.next(oid))) { - TrackedNode &tobj = tracked_nodes[*oid]; + for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) { + TrackedNode &tobj = E.value; tobj.net_id = 0; tobj.remote_peer = 0; tobj.last_sync = 0; @@ -195,9 +193,8 @@ Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) { ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); peers_info[p_peer].known_nodes.insert(p_id); } else { - const int *pid = nullptr; - while ((pid = peers_info.next(pid))) { - peers_info.get(*pid).known_nodes.insert(p_id); + for (KeyValue<int, PeerInfo> &E : peers_info) { + E.value.known_nodes.insert(p_id); } } return OK; @@ -208,9 +205,8 @@ Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) { ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); peers_info[p_peer].known_nodes.erase(p_id); } else { - const int *pid = nullptr; - while ((pid = peers_info.next(pid))) { - peers_info.get(*pid).known_nodes.erase(p_id); + for (KeyValue<int, PeerInfo> &E : peers_info) { + E.value.known_nodes.erase(p_id); } } return OK; diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h index 18e4d9fa39..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 032f43d9b9..6bce957d80 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -129,6 +129,7 @@ #include "scene/main/canvas_layer.h" #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/main/resource_preloader.h" #include "scene/main/scene_tree.h" #include "scene/main/timer.h" @@ -139,6 +140,7 @@ #include "scene/multiplayer/scene_cache_interface.h" #include "scene/multiplayer/scene_replication_interface.h" #include "scene/multiplayer/scene_rpc_interface.h" +#include "scene/resources/animation_library.h" #include "scene/resources/audio_stream_sample.h" #include "scene/resources/bit_map.h" #include "scene/resources/box_shape_3d.h" @@ -225,6 +227,7 @@ #include "scene/3d/gpu_particles_collision_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/joint_3d.h" +#include "scene/3d/label_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" @@ -300,6 +303,7 @@ void register_scene_types() { GDREGISTER_CLASS(Object); GDREGISTER_CLASS(Node); + GDREGISTER_VIRTUAL_CLASS(MissingNode); GDREGISTER_ABSTRACT_CLASS(InstancePlaceholder); GDREGISTER_ABSTRACT_CLASS(Viewport); @@ -479,6 +483,7 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(SpriteBase3D); GDREGISTER_CLASS(Sprite3D); GDREGISTER_CLASS(AnimatedSprite3D); + GDREGISTER_CLASS(Label3D); GDREGISTER_ABSTRACT_CLASS(Light3D); GDREGISTER_CLASS(DirectionalLight3D); GDREGISTER_CLASS(OmniLight3D); @@ -575,6 +580,7 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeColorConstant); GDREGISTER_CLASS(VisualShaderNodeVec2Constant); GDREGISTER_CLASS(VisualShaderNodeVec3Constant); + GDREGISTER_CLASS(VisualShaderNodeVec4Constant); GDREGISTER_CLASS(VisualShaderNodeTransformConstant); GDREGISTER_CLASS(VisualShaderNodeFloatOp); GDREGISTER_CLASS(VisualShaderNodeIntOp); @@ -619,6 +625,7 @@ void register_scene_types() { GDREGISTER_CLASS(VisualShaderNodeColorUniform); GDREGISTER_CLASS(VisualShaderNodeVec2Uniform); GDREGISTER_CLASS(VisualShaderNodeVec3Uniform); + GDREGISTER_CLASS(VisualShaderNodeVec4Uniform); GDREGISTER_CLASS(VisualShaderNodeTransformUniform); GDREGISTER_CLASS(VisualShaderNodeTextureUniform); GDREGISTER_CLASS(VisualShaderNodeTextureUniformTriplanar); @@ -747,6 +754,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(Mesh); GDREGISTER_CLASS(ArrayMesh); + GDREGISTER_CLASS(PlaceholderMesh); GDREGISTER_CLASS(ImmediateMesh); GDREGISTER_CLASS(MultiMesh); GDREGISTER_CLASS(SurfaceTool); @@ -761,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); @@ -768,6 +777,7 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(BaseMaterial3D); GDREGISTER_CLASS(StandardMaterial3D); GDREGISTER_CLASS(ORMMaterial3D); + GDREGISTER_CLASS(PlaceholderMaterial); SceneTree::add_idle_callback(BaseMaterial3D::flush_changes); BaseMaterial3D::init_shaders(); @@ -832,8 +842,15 @@ void register_scene_types() { GDREGISTER_CLASS(CompressedCubemap); GDREGISTER_CLASS(CompressedCubemapArray); GDREGISTER_CLASS(CompressedTexture2DArray); + GDREGISTER_CLASS(PlaceholderTexture2D); + GDREGISTER_CLASS(PlaceholderTexture3D); + GDREGISTER_ABSTRACT_CLASS(PlaceholderTextureLayered); + GDREGISTER_CLASS(PlaceholderTexture2DArray); + GDREGISTER_CLASS(PlaceholderCubemap); + GDREGISTER_CLASS(PlaceholderCubemapArray); GDREGISTER_CLASS(Animation); + GDREGISTER_CLASS(AnimationLibrary); GDREGISTER_CLASS(FontData); GDREGISTER_CLASS(Font); GDREGISTER_CLASS(Curve); @@ -896,18 +913,16 @@ void register_scene_types() { #ifndef DISABLE_DEPRECATED // Dropped in 4.0, near approximation. ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree"); - ClassDB::add_compatibility_class("AStar", "AStar3D"); + ClassDB::add_compatibility_class("BakedLightmap", "LightmapGI"); + ClassDB::add_compatibility_class("BakedLightmapData", "LightmapGIData"); ClassDB::add_compatibility_class("BitmapFont", "Font"); ClassDB::add_compatibility_class("DynamicFont", "Font"); ClassDB::add_compatibility_class("DynamicFontData", "FontData"); - ClassDB::add_compatibility_class("ToolButton", "Button"); ClassDB::add_compatibility_class("Navigation3D", "Node3D"); ClassDB::add_compatibility_class("Navigation2D", "Node2D"); + ClassDB::add_compatibility_class("OpenSimplexNoise", "FastNoiseLite"); + ClassDB::add_compatibility_class("ToolButton", "Button"); ClassDB::add_compatibility_class("YSort", "Node2D"); - ClassDB::add_compatibility_class("GIProbe", "VoxelGI"); - ClassDB::add_compatibility_class("GIProbeData", "VoxelGIData"); - ClassDB::add_compatibility_class("BakedLightmap", "LightmapGI"); - ClassDB::add_compatibility_class("BakedLightmapData", "LightmapGIData"); // Portal and room occlusion was replaced by raster occlusion (OccluderInstance3D node). ClassDB::add_compatibility_class("Portal", "Node3D"); ClassDB::add_compatibility_class("Room", "Node3D"); @@ -928,6 +943,7 @@ void register_scene_types() { ClassDB::add_compatibility_class("ARVROrigin", "XROrigin3D"); ClassDB::add_compatibility_class("ARVRPositionalTracker", "XRPositionalTracker"); ClassDB::add_compatibility_class("ARVRServer", "XRServer"); + ClassDB::add_compatibility_class("AStar", "AStar3D"); ClassDB::add_compatibility_class("BoneAttachment", "BoneAttachment3D"); ClassDB::add_compatibility_class("BoxShape", "BoxShape3D"); ClassDB::add_compatibility_class("Camera", "Camera3D"); @@ -955,6 +971,8 @@ void register_scene_types() { ClassDB::add_compatibility_class("EditorSpatialGizmo", "EditorNode3DGizmo"); ClassDB::add_compatibility_class("EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin"); ClassDB::add_compatibility_class("Generic6DOFJoint", "Generic6DOFJoint3D"); + ClassDB::add_compatibility_class("GIProbe", "VoxelGI"); + ClassDB::add_compatibility_class("GIProbeData", "VoxelGIData"); ClassDB::add_compatibility_class("GradientTexture", "GradientTexture1D"); ClassDB::add_compatibility_class("HeightMapShape", "HeightMapShape3D"); ClassDB::add_compatibility_class("HingeJoint", "HingeJoint3D"); @@ -1078,8 +1096,6 @@ void register_scene_types() { SceneReplicationInterface::make_default(); SceneRPCInterface::make_default(); SceneCacheInterface::make_default(); - - NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); } void initialize_theme() { @@ -1102,6 +1118,9 @@ void initialize_theme() { TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO); ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false); + const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false); + Ref<Font> font; if (!font_path.is_empty()) { font = ResourceLoader::load(font_path); @@ -1112,7 +1131,7 @@ void initialize_theme() { // Always make the default theme to avoid invalid default font/icon/style in the given theme. if (RenderingServer::get_singleton()) { - make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiased); + make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiased, font_msdf, font_generate_mipmaps); } if (!theme_path.is_empty()) { @@ -1129,8 +1148,6 @@ void initialize_theme() { } void unregister_scene_types() { - NativeExtensionManager::get_singleton()->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE); - SceneDebugger::deinitialize(); clear_default_theme(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index f6e0df0265..a26aa10f42 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -3428,7 +3428,6 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { real_t duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes real_t low = 0.0; // 0% of the current animation segment real_t high = 1.0; // 100% of the current animation segment - real_t middle; Vector2 start(0, bt->values[idx].value.value); Vector2 start_out = start + bt->values[idx].value.out_handle; @@ -3437,7 +3436,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { //narrow high and low as much as possible for (int i = 0; i < iterations; i++) { - middle = (low + high) / 2; + real_t middle = (low + high) / 2; Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end); @@ -3456,7 +3455,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const { return low_pos.lerp(high_pos, c).y; } -int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset, real_t p_end_offset) { +int Animation::audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset, real_t p_end_offset) { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; ERR_FAIL_COND_V(t->type != TYPE_AUDIO, -1); @@ -3482,7 +3481,7 @@ int Animation::audio_track_insert_key(int p_track, double p_time, const RES &p_s return key; } -void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream) { +void Animation::audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_AUDIO); @@ -3532,14 +3531,14 @@ void Animation::audio_track_set_key_end_offset(int p_track, int p_key, real_t p_ emit_changed(); } -RES Animation::audio_track_get_key_stream(int p_track, int p_key) const { - ERR_FAIL_INDEX_V(p_track, tracks.size(), RES()); +Ref<Resource> Animation::audio_track_get_key_stream(int p_track, int p_key) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), Ref<Resource>()); const Track *t = tracks[p_track]; - ERR_FAIL_COND_V(t->type != TYPE_AUDIO, RES()); + ERR_FAIL_COND_V(t->type != TYPE_AUDIO, Ref<Resource>()); const AudioTrack *at = static_cast<const AudioTrack *>(t); - ERR_FAIL_INDEX_V(p_key, at->values.size(), RES()); + ERR_FAIL_INDEX_V(p_key, at->values.size(), Ref<Resource>()); return at->values[p_key].value.stream; } @@ -3829,7 +3828,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0)); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step"); ADD_SIGNAL(MethodInfo("tracks_changed")); diff --git a/scene/resources/animation.h b/scene/resources/animation.h index f9a33da428..b4528ccd3a 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -180,7 +180,7 @@ private: /* AUDIO TRACK */ struct AudioKey { - RES stream; + Ref<Resource> stream; real_t start_offset = 0.0; //offset from start real_t end_offset = 0.0; //offset from end, if 0 then full length or infinite AudioKey() { @@ -436,11 +436,11 @@ public: real_t bezier_track_interpolate(int p_track, double p_time) const; - int audio_track_insert_key(int p_track, double p_time, const RES &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0); - void audio_track_set_key_stream(int p_track, int p_key, const RES &p_stream); + int audio_track_insert_key(int p_track, double p_time, const Ref<Resource> &p_stream, real_t p_start_offset = 0, real_t p_end_offset = 0); + void audio_track_set_key_stream(int p_track, int p_key, const Ref<Resource> &p_stream); void audio_track_set_key_start_offset(int p_track, int p_key, real_t p_offset); void audio_track_set_key_end_offset(int p_track, int p_key, real_t p_offset); - RES audio_track_get_key_stream(int p_track, int p_key) const; + Ref<Resource> audio_track_get_key_stream(int p_track, int p_key) const; real_t audio_track_get_key_start_offset(int p_track, int p_key) const; real_t audio_track_get_key_end_offset(int p_track, int p_key) const; diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp new file mode 100644 index 0000000000..2a581fb126 --- /dev/null +++ b/scene/resources/animation_library.cpp @@ -0,0 +1,151 @@ +/*************************************************************************/ +/* animation_library.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "animation_library.h" + +bool AnimationLibrary::is_valid_animation_name(const String &p_name) { + return !(p_name.is_empty() || p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); +} + +bool AnimationLibrary::is_valid_library_name(const String &p_name) { + return !(p_name.contains("/") || p_name.contains(":") || p_name.contains(",") || p_name.contains("[")); +} + +String AnimationLibrary::validate_library_name(const String &p_name) { + String name = p_name; + const char *characters = "/:,["; + for (const char *p = characters; *p; p++) { + name = name.replace(String::chr(*p), "_"); + } + return name; +} + +Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) { + ERR_FAIL_COND_V_MSG(!is_valid_animation_name(p_name), ERR_INVALID_PARAMETER, "Invalid animation name: '" + String(p_name) + "'."); + ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); + + if (animations.has(p_name)) { + animations.erase(p_name); + emit_signal(SNAME("animation_removed"), p_name); + } + + animations.insert(p_name, p_animation); + emit_signal(SNAME("animation_added"), p_name); + notify_property_list_changed(); + return OK; +} + +void AnimationLibrary::remove_animation(const StringName &p_name) { + ERR_FAIL_COND(!animations.has(p_name)); + + animations.erase(p_name); + emit_signal(SNAME("animation_removed"), p_name); + notify_property_list_changed(); +} + +void AnimationLibrary::rename_animation(const StringName &p_name, const StringName &p_new_name) { + ERR_FAIL_COND(!animations.has(p_name)); + ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); + ERR_FAIL_COND(animations.has(p_new_name)); + + animations.insert(p_new_name, animations[p_name]); + animations.erase(p_name); + emit_signal(SNAME("animation_renamed"), p_name, p_new_name); +} + +bool AnimationLibrary::has_animation(const StringName &p_name) const { + return animations.has(p_name); +} + +Ref<Animation> AnimationLibrary::get_animation(const StringName &p_name) const { + ERR_FAIL_COND_V(!animations.has(p_name), Ref<Animation>()); + + return animations[p_name]; +} + +TypedArray<StringName> AnimationLibrary::_get_animation_list() const { + TypedArray<StringName> ret; + List<StringName> names; + get_animation_list(&names); + for (const StringName &K : names) { + ret.push_back(K); + } + return ret; +} + +void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const { + List<StringName> anims; + + for (const KeyValue<StringName, Ref<Animation>> &E : animations) { + anims.push_back(E.key); + } + + anims.sort_custom<StringName::AlphCompare>(); + + for (const StringName &E : anims) { + p_animations->push_back(E); + } +} + +void AnimationLibrary::_set_data(const Dictionary &p_data) { + animations.clear(); + List<Variant> keys; + p_data.get_key_list(&keys); + for (const Variant &K : keys) { + add_animation(K, p_data[K]); + } +} + +Dictionary AnimationLibrary::_get_data() const { + Dictionary ret; + for (const KeyValue<StringName, Ref<Animation>> &K : animations) { + ret[K.key] = K.value; + } + return ret; +} + +void AnimationLibrary::_bind_methods() { + ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationLibrary::add_animation); + ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationLibrary::remove_animation); + ClassDB::bind_method(D_METHOD("rename_animation", "name", "newname"), &AnimationLibrary::rename_animation); + ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation); + ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation); + ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data"); + ADD_SIGNAL(MethodInfo("animation_added", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation"))); + ADD_SIGNAL(MethodInfo("animation_removed", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation"))); + ADD_SIGNAL(MethodInfo("animation_renamed", PropertyInfo(Variant::OBJECT, "name", PROPERTY_HINT_RESOURCE_TYPE, "Animation"), PropertyInfo(Variant::OBJECT, "to_name", PROPERTY_HINT_RESOURCE_TYPE, "Animation"))); +} +AnimationLibrary::AnimationLibrary() { +} diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h new file mode 100644 index 0000000000..7a69cd140a --- /dev/null +++ b/scene/resources/animation_library.h @@ -0,0 +1,66 @@ +/*************************************************************************/ +/* animation_library.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANIMATION_LIBRARY_H +#define ANIMATION_LIBRARY_H + +#include "core/variant/typed_array.h" +#include "scene/resources/animation.h" + +class AnimationLibrary : public Resource { + GDCLASS(AnimationLibrary, Resource) + + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + + TypedArray<StringName> _get_animation_list() const; + + friend class AnimationPlayer; //for faster access + HashMap<StringName, Ref<Animation>> animations; + +protected: + static void _bind_methods(); + +public: + static bool is_valid_animation_name(const String &p_name); + static bool is_valid_library_name(const String &p_name); + static String validate_library_name(const String &p_name); + + Error add_animation(const StringName &p_name, const Ref<Animation> &p_animation); + void remove_animation(const StringName &p_name); + void rename_animation(const StringName &p_name, const StringName &p_new_name); + bool has_animation(const StringName &p_name) const; + Ref<Animation> get_animation(const StringName &p_name) const; + void get_animation_list(List<StringName> *p_animations) const; + + AnimationLibrary(); +}; + +#endif // ANIMATIONLIBRARY_H diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 56786ac4b1..30c222bdff 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -556,9 +556,9 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { file_path += ".wav"; } - FileAccessRef file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE); //Overrides existing file if present - ERR_FAIL_COND_V(!file, ERR_FILE_CANT_WRITE); + ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_WRITE); // Create WAV Header file->store_string("RIFF"); //ChunkID @@ -596,8 +596,6 @@ Error AudioStreamSample::save_to_wav(const String &p_path) { break; } - file->close(); - return OK; } 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 e40e4392cb..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; @@ -107,10 +110,10 @@ private: LightMode light_mode = LIGHT_MODE_NORMAL; bool particles_animation = false; - // Initialized in the constructor. - int particles_anim_h_frames; - int particles_anim_v_frames; - bool particles_anim_loop; + // Proper values set in constructor. + int particles_anim_h_frames = 0; + int particles_anim_v_frames = 0; + bool particles_anim_loop = false; protected: static void _bind_methods(); diff --git a/scene/resources/concave_polygon_shape_3d.cpp b/scene/resources/concave_polygon_shape_3d.cpp index 3e178108c4..ab2c1da327 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,7 +50,7 @@ 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()) { + for (RBSet<DrawEdge>::Element *E = edges.front(); E; E = E->next()) { points.write[idx + 0] = E->get().a; points.write[idx + 1] = E->get().b; idx += 2; diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 667399ee75..c416a03f38 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -82,7 +82,7 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) { if (is_collision_outline_enabled()) { RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color, 1.0, true); } } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 6485c1ac77..34cbd3a360 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -632,7 +632,7 @@ 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::_bake_segment2d(HashMap<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); @@ -681,7 +681,8 @@ void Curve2D::_bake() const { List<Vector2> pointlist; List<real_t> distlist; - pointlist.push_back(position); //start always from origin + // Start always from origin. + pointlist.push_back(position); distlist.push_back(0.0); for (int i = 0; i < points.size() - 1; i++) { @@ -728,15 +729,18 @@ void Curve2D::_bake() const { p = np; } } - } - Vector2 lastpos = points[points.size() - 1].position; + Vector2 npp = points[i + 1].position; + real_t d = position.distance_to(npp); + + position = npp; + dist += d; + + pointlist.push_back(position); + distlist.push_back(dist); + } - real_t rem = position.distance_to(lastpos); - dist += rem; baked_max_ofs = dist; - pointlist.push_back(lastpos); - distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); baked_dist_cache.resize(distlist.size()); @@ -763,7 +767,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); @@ -771,18 +775,19 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { return baked_point_cache.get(0); } - int bpc = baked_point_cache.size(); const Vector2 *r = baked_point_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int start = 0, end = bpc, idx = (end + start) / 2; - // binary search to find baked points + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. while (start < idx) { real_t offset = baked_dist_cache[idx]; if (p_offset <= offset) { @@ -803,7 +808,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { if (p_cubic) { Vector2 pre = idx > 0 ? r[idx - 1] : r[idx]; - Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; + Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1]; return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac); } else { return r[idx].lerp(r[idx + 1], frac); @@ -829,13 +834,13 @@ real_t Curve2D::get_bake_interval() const { } Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); @@ -867,13 +872,13 @@ Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { } real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve2D."); @@ -949,7 +954,7 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con if (points.size() == 0) { return tess; } - Vector<Map<real_t, Vector2>> midpoints; + Vector<HashMap<real_t, Vector2>> midpoints; midpoints.resize(points.size() - 1); @@ -1009,11 +1014,7 @@ void Curve2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } -Curve2D::Curve2D() { - /* add_point(Vector2(-1,0,0)); - add_point(Vector2(0,2,0)); - add_point(Vector2(0,3,5));*/ -} +Curve2D::Curve2D() {} /***********************************************************************************/ /***********************************************************************************/ @@ -1136,7 +1137,7 @@ 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::_bake_segment3d(HashMap<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); @@ -1194,6 +1195,7 @@ void Curve3D::_bake() const { List<Plane> pointlist; List<real_t> distlist; + // Start always from origin. pointlist.push_back(Plane(position, points[0].tilt)); distlist.push_back(0.0); @@ -1244,16 +1246,22 @@ void Curve3D::_bake() const { p = np; } } - } - Vector3 lastpos = points[points.size() - 1].position; - real_t lastilt = points[points.size() - 1].tilt; + Vector3 npp = points[i + 1].position; + real_t d = position.distance_to(npp); + + position = npp; + Plane post; + post.normal = position; + post.d = points[i + 1].tilt; + + dist += d; + + pointlist.push_back(post); + distlist.push_back(dist); + } - real_t rem = position.distance_to(lastpos); - dist += rem; baked_max_ofs = dist; - pointlist.push_back(Plane(lastpos, lastilt)); - distlist.push_back(dist); baked_point_cache.resize(pointlist.size()); Vector3 *w = baked_point_cache.ptrw(); @@ -1328,7 +1336,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); @@ -1336,18 +1344,19 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { return baked_point_cache.get(0); } - int bpc = baked_point_cache.size(); const Vector3 *r = baked_point_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int start = 0, end = bpc, idx = (end + start) / 2; - // binary search to find baked points + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. while (start < idx) { real_t offset = baked_dist_cache[idx]; if (p_offset <= offset) { @@ -1368,7 +1377,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { if (p_cubic) { Vector3 pre = idx > 0 ? r[idx - 1] : r[idx]; - Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1]; + Vector3 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1]; return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac); } else { return r[idx].lerp(r[idx + 1], frac); @@ -1380,7 +1389,7 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { _bake(); } - //validate// + // Validate: Curve may not have baked tilts. int pc = baked_tilt_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); @@ -1388,29 +1397,37 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { return baked_tilt_cache.get(0); } - int bpc = baked_tilt_cache.size(); const real_t *r = baked_tilt_cache.ptr(); if (p_offset < 0) { return r[0]; } if (p_offset >= baked_max_ofs) { - return r[bpc - 1]; + return r[pc - 1]; } - int idx = Math::floor((double)p_offset / (double)bake_interval); - real_t frac = Math::fmod(p_offset, bake_interval); - - if (idx >= bpc - 1) { - return r[bpc - 1]; - } else if (idx == bpc - 2) { - if (frac > 0) { - frac /= Math::fmod(baked_max_ofs, bake_interval); + int start = 0; + int end = pc; + int idx = (end + start) / 2; + // Binary search to find baked points. + while (start < idx) { + real_t offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; } - } else { - frac /= bake_interval; + idx = (end + start) / 2; } + real_t offset_begin = baked_dist_cache[idx]; + real_t offset_end = baked_dist_cache[idx + 1]; + + real_t idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment"); + + real_t frac = (p_offset - offset_begin) / idx_interval; + return Math::lerp(r[idx], r[idx + 1], (real_t)frac); } @@ -1419,8 +1436,7 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) _bake(); } - //validate// - // curve may not have baked up vectors + // Validate: Curve may not have baked up vectors. int count = baked_up_vector_cache.size(); ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D."); @@ -1432,10 +1448,27 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const Vector3 *rp = baked_point_cache.ptr(); const real_t *rt = baked_tilt_cache.ptr(); - real_t offset = CLAMP(p_offset, 0.0f, baked_max_ofs); + int start = 0; + int end = count; + int idx = (end + start) / 2; + // Binary search to find baked points. + while (start < idx) { + real_t offset = baked_dist_cache[idx]; + if (p_offset <= offset) { + end = idx; + } else { + start = idx; + } + idx = (end + start) / 2; + } + + real_t offset_begin = baked_dist_cache[idx]; + real_t offset_end = baked_dist_cache[idx + 1]; - int idx = Math::floor((double)offset / (double)bake_interval); - real_t frac = Math::fmod(offset, bake_interval) / bake_interval; + real_t idx_interval = offset_end - offset_begin; + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment"); + + real_t frac = (p_offset - offset_begin) / idx_interval; if (idx == count - 1) { return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx]; @@ -1486,13 +1519,13 @@ PackedVector3Array Curve3D::get_baked_up_vectors() const { } Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); @@ -1524,13 +1557,13 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { } real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { - // Brute force method + // Brute force method. if (baked_cache_dirty) { _bake(); } - //validate// + // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve3D."); @@ -1635,7 +1668,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<HashMap<real_t, Vector3>> midpoints; midpoints.resize(points.size() - 1); @@ -1705,8 +1738,4 @@ void Curve3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled"); } -Curve3D::Curve3D() { - /* add_point(Vector3(-1,0,0)); - add_point(Vector3(0,2,0)); - add_point(Vector3(0,3,5));*/ -} +Curve3D::Curve3D() {} diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 767900b843..f9b172f175 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -168,7 +168,7 @@ class Curve2D : public Resource { 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(HashMap<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); @@ -233,7 +233,7 @@ class Curve3D : public Resource { 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(HashMap<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); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index eb71401a3a..4fb44446d9 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -177,7 +177,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1)); theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4)); - theme->set_constant("hseparation", "Button", 2 * scale); + theme->set_constant("h_separation", "Button", 2 * scale); // LinkButton @@ -230,7 +230,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color); theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "OptionButton", 2 * scale); + theme->set_constant("h_separation", "OptionButton", 2 * scale); theme->set_constant("arrow_margin", "OptionButton", 4 * scale); theme->set_constant("outline_size", "OptionButton", 0); @@ -252,7 +252,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3)); theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "MenuButton", 3 * scale); + theme->set_constant("h_separation", "MenuButton", 3 * scale); theme->set_constant("outline_size", "MenuButton", 0); // CheckBox @@ -295,8 +295,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color); theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1)); - theme->set_constant("hseparation", "CheckBox", 4 * scale); - theme->set_constant("check_vadjust", "CheckBox", 0 * scale); + theme->set_constant("h_separation", "CheckBox", 4 * scale); + theme->set_constant("check_v_adjust", "CheckBox", 0 * scale); theme->set_constant("outline_size", "CheckBox", 0); // CheckButton @@ -335,8 +335,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color); theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "CheckButton", 4 * scale); - theme->set_constant("check_vadjust", "CheckButton", 0 * scale); + theme->set_constant("h_separation", "CheckButton", 4 * scale); + theme->set_constant("check_v_adjust", "CheckButton", 0 * scale); theme->set_constant("outline_size", "CheckButton", 0); // Label @@ -582,8 +582,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("close", "Window", icons["close"]); theme->set_icon("close_pressed", "Window", icons["close_hl"]); - theme->set_constant("close_h_ofs", "Window", 18 * scale); - theme->set_constant("close_v_ofs", "Window", 24 * scale); + theme->set_constant("close_h_offset", "Window", 18 * scale); + theme->set_constant("close_v_offset", "Window", 24 * scale); // Dialogs @@ -649,11 +649,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("unchecked", "PopupMenu", icons["unchecked"]); theme->set_icon("radio_checked", "PopupMenu", icons["radio_checked"]); theme->set_icon("radio_unchecked", "PopupMenu", icons["radio_unchecked"]); - theme->set_icon("submenu", "PopupMenu", icons["arrow_right"]); - theme->set_icon("submenu_mirrored", "PopupMenu", icons["arrow_left"]); + theme->set_icon("submenu", "PopupMenu", icons["popup_menu_arrow_right"]); + theme->set_icon("submenu_mirrored", "PopupMenu", icons["popup_menu_arrow_left"]); theme->set_font("font", "PopupMenu", Ref<Font>()); + theme->set_font("font_separator", "PopupMenu", Ref<Font>()); theme->set_font_size("font_size", "PopupMenu", -1); + theme->set_font_size("font_separator_size", "PopupMenu", -1); theme->set_color("font_color", "PopupMenu", control_font_color); theme->set_color("font_accelerator_color", "PopupMenu", Color(0.7, 0.7, 0.7, 0.8)); @@ -661,10 +663,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_hover_color", "PopupMenu", control_font_color); theme->set_color("font_separator_color", "PopupMenu", control_font_color); theme->set_color("font_outline_color", "PopupMenu", Color(1, 1, 1)); + theme->set_color("font_separator_outline_color", "PopupMenu", Color(1, 1, 1)); - theme->set_constant("hseparation", "PopupMenu", 4 * scale); - theme->set_constant("vseparation", "PopupMenu", 4 * scale); + theme->set_constant("h_separation", "PopupMenu", 4 * scale); + theme->set_constant("v_separation", "PopupMenu", 4 * scale); theme->set_constant("outline_size", "PopupMenu", 0); + theme->set_constant("separator_outline_size", "PopupMenu", 0); theme->set_constant("item_start_padding", "PopupMenu", 2 * scale); theme->set_constant("item_end_padding", "PopupMenu", 2 * scale); @@ -682,13 +686,15 @@ 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("selectedframe", "GraphNode", graphnode_selected); + theme->set_stylebox("selected_frame", "GraphNode", graphnode_selected); theme->set_stylebox("comment", "GraphNode", graphnode_comment_normal); - theme->set_stylebox("commentfocus", "GraphNode", graphnode_comment_selected); + 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"]); @@ -700,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 @@ -742,8 +749,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27)); theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color); - theme->set_constant("hseparation", "Tree", 4 * scale); - theme->set_constant("vseparation", "Tree", 4 * scale); + theme->set_constant("h_separation", "Tree", 4 * scale); + theme->set_constant("v_separation", "Tree", 4 * scale); theme->set_constant("item_margin", "Tree", 16 * scale); theme->set_constant("button_margin", "Tree", 4 * scale); theme->set_constant("draw_relationship_lines", "Tree", 0); @@ -760,8 +767,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("bg", "ItemList", make_flat_stylebox(style_normal_color)); theme->set_stylebox("bg_focus", "ItemList", focus); - theme->set_constant("hseparation", "ItemList", 4); - theme->set_constant("vseparation", "ItemList", 2); + theme->set_constant("h_separation", "ItemList", 4); + theme->set_constant("v_separation", "ItemList", 2); theme->set_constant("icon_margin", "ItemList", 4); theme->set_constant("line_separation", "ItemList", 2 * scale); @@ -842,7 +849,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_outline_color", "TabBar", Color(1, 1, 1)); theme->set_color("drop_mark_color", "TabBar", Color(1, 1, 1)); - theme->set_constant("hseparation", "TabBar", 4 * scale); + theme->set_constant("h_separation", "TabBar", 4 * scale); theme->set_constant("outline_size", "TabBar", 0); // Separators @@ -892,7 +899,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3)); theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1)); - theme->set_constant("hseparation", "ColorPickerButton", 2 * scale); + theme->set_constant("h_separation", "ColorPickerButton", 2 * scale); theme->set_constant("outline_size", "ColorPickerButton", 0); // ColorPresetButton @@ -952,8 +959,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale); theme->set_constant("line_separation", "RichTextLabel", 0 * scale); - theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale); - theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale); + theme->set_constant("table_h_separation", "RichTextLabel", 3 * scale); + theme->set_constant("table_v_separation", "RichTextLabel", 3 * scale); theme->set_constant("outline_size", "RichTextLabel", 0); @@ -972,16 +979,16 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("margin_top", "MarginContainer", 0 * scale); theme->set_constant("margin_right", "MarginContainer", 0 * scale); theme->set_constant("margin_bottom", "MarginContainer", 0 * scale); - theme->set_constant("hseparation", "GridContainer", 4 * scale); - theme->set_constant("vseparation", "GridContainer", 4 * scale); + theme->set_constant("h_separation", "GridContainer", 4 * scale); + theme->set_constant("v_separation", "GridContainer", 4 * scale); theme->set_constant("separation", "HSplitContainer", 12 * scale); theme->set_constant("separation", "VSplitContainer", 12 * scale); theme->set_constant("autohide", "HSplitContainer", 1 * scale); theme->set_constant("autohide", "VSplitContainer", 1 * scale); - theme->set_constant("hseparation", "HFlowContainer", 4 * scale); - theme->set_constant("vseparation", "HFlowContainer", 4 * scale); - theme->set_constant("hseparation", "VFlowContainer", 4 * scale); - theme->set_constant("vseparation", "VFlowContainer", 4 * scale); + theme->set_constant("h_separation", "HFlowContainer", 4 * scale); + theme->set_constant("v_separation", "HFlowContainer", 4 * scale); + theme->set_constant("h_separation", "VFlowContainer", 4 * scale); + theme->set_constant("v_separation", "VFlowContainer", 4 * scale); theme->set_stylebox("panel", "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0)); @@ -1022,7 +1029,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const default_style = make_flat_stylebox(Color(1, 0.365, 0.365), 4, 4, 4, 4, 0, false, 2); } -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa) { +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel, TextServer::Hinting p_font_hinting, bool p_font_antialiased, bool p_font_msdf, bool p_font_generate_mipmaps) { Ref<Theme> t; t.instantiate(); @@ -1047,9 +1054,12 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos Ref<FontData> dynamic_font_data; dynamic_font_data.instantiate(); dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); - dynamic_font_data->set_subpixel_positioning(p_subpixel); - dynamic_font_data->set_hinting(p_hinting); - dynamic_font_data->set_antialiased(p_aa); + dynamic_font_data->set_subpixel_positioning(p_font_subpixel); + dynamic_font_data->set_hinting(p_font_hinting); + dynamic_font_data->set_antialiased(p_font_antialiased); + dynamic_font_data->set_multichannel_signed_distance_field(p_font_msdf); + dynamic_font_data->set_generate_mipmaps(p_font_generate_mipmaps); + dynamic_font->add_data(dynamic_font_data); default_font = dynamic_font; diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index a9e21dda3f..f777330a07 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -36,7 +36,7 @@ const int default_font_size = 16; void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); -void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_subpixel, TextServer::Hinting p_hinting, bool p_aa); +void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, bool p_font_antialiased = true, bool p_font_msdf = false, bool p_font_generate_mipmaps = false); void clear_default_theme(); #endif diff --git a/scene/resources/default_theme/popup_menu_arrow_left.svg b/scene/resources/default_theme/popup_menu_arrow_left.svg new file mode 100644 index 0000000000..8fae265a3b --- /dev/null +++ b/scene/resources/default_theme/popup_menu_arrow_left.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m5.4999793 11-3-3 3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg> diff --git a/scene/resources/default_theme/popup_menu_arrow_right.svg b/scene/resources/default_theme/popup_menu_arrow_right.svg new file mode 100644 index 0000000000..03f05fc46e --- /dev/null +++ b/scene/resources/default_theme/popup_menu_arrow_right.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" height="16" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 8 16" width="8" xmlns="http://www.w3.org/2000/svg"><path d="m2.5000207 11 3-3-3-3" fill="none" stroke="#b2b2b2" stroke-opacity=".45" stroke-width="2"/></svg> diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 78698835fc..5d1e07f6cd 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -189,6 +189,7 @@ void Environment::_update_ambient_light() { void Environment::set_tonemapper(ToneMapper p_tone_mapper) { tone_mapper = p_tone_mapper; _update_tonemap(); + notify_property_list_changed(); } Environment::ToneMapper Environment::get_tonemapper() const { @@ -1049,6 +1050,10 @@ void Environment::_validate_property(PropertyInfo &property) const { } } + if (property.name == "tonemap_white" && tone_mapper == TONE_MAPPER_LINEAR) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } + if (property.name == "glow_intensity" && glow_blend_mode == GLOW_BLEND_MODE_MIX) { property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; } @@ -1081,6 +1086,7 @@ void Environment::_validate_property(PropertyInfo &property) const { "auto_exposure_", "ssr_", "ssao_", + "ssil_", "sdfgi_", "glow_", "adjustment_", @@ -1090,7 +1096,6 @@ void Environment::_validate_property(PropertyInfo &property) const { static const char *high_end_prefixes[] = { "auto_exposure_", - "tonemap_", "ssr_", "ssao_", nullptr diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 6e6ee7d3ac..d586abac14 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -33,6 +33,7 @@ #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/string/translation.h" +#include "core/templates/hash_map.h" #include "core/templates/hashfuncs.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" @@ -54,6 +55,7 @@ _FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const { cache.write[p_cache_index] = TS->create_font(); TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); TS->font_set_msdf_size(cache[p_cache_index], msdf_size); @@ -77,6 +79,9 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased); + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontData::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontData::get_generate_mipmaps); + ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name); ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name); @@ -212,6 +217,7 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); @@ -442,6 +448,7 @@ void FontData::reset_state() { cache.clear(); antialiased = true; + mipmaps = false; msdf = false; force_autohinter = false; hinting = TextServer::HINTING_LIGHT; @@ -735,13 +742,14 @@ Error FontData::load_bitmap_font(const String &p_path) { reset_state(); antialiased = false; + mipmaps = false; msdf = false; force_autohinter = false; hinting = TextServer::HINTING_NONE; oversampling = 1.0f; - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_CREATE, vformat(RTR("Cannot open font from file: %s."), p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, vformat(RTR("Cannot open font from file: %s."), p_path)); int base_size = 16; int height = 0; @@ -956,7 +964,7 @@ Error FontData::load_bitmap_font(const String &p_path) { int delimiter = line.find(" "); String type = line.substr(0, delimiter); int pos = delimiter + 1; - Map<String, String> keys; + HashMap<String, String> keys; while (pos < line.size() && line[pos] == ' ') { pos++; @@ -1290,6 +1298,21 @@ bool FontData::is_antialiased() const { return antialiased; } +void FontData::set_generate_mipmaps(bool p_generate_mipmaps) { + if (mipmaps != p_generate_mipmaps) { + mipmaps = p_generate_mipmaps; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_generate_mipmaps(cache[i], mipmaps); + } + emit_changed(); + } +} + +bool FontData::get_generate_mipmaps() const { + return mipmaps; +} + void FontData::set_multichannel_signed_distance_field(bool p_msdf) { if (msdf != p_msdf) { msdf = p_msdf; diff --git a/scene/resources/font.h b/scene/resources/font.h index 2aa12dd2de..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" @@ -49,6 +49,7 @@ class FontData : public Resource { PackedByteArray data; bool antialiased = true; + bool mipmaps = false; bool msdf = false; int msdf_pixel_range = 16; int msdf_size = 48; @@ -103,6 +104,9 @@ public: virtual void set_antialiased(bool p_antialiased); virtual bool is_antialiased() const; + virtual void set_generate_mipmaps(bool p_generate_mipmaps); + virtual bool get_generate_mipmaps() const; + virtual void set_multichannel_signed_distance_field(bool p_msdf); virtual bool is_multichannel_signed_distance_field() const; diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index caaa3d8628..a9c44dc6bf 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -157,7 +157,7 @@ void Gradient::reverse() { emit_signal(CoreStringNames::get_singleton()->changed); } -void Gradient::set_points(Vector<Gradient::Point> &p_points) { +void Gradient::set_points(const Vector<Gradient::Point> &p_points) { points = p_points; is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index c2085b3a13..a3d3449099 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -73,7 +73,7 @@ public: void add_point(float p_offset, const Color &p_color); void remove_point(int p_index); - void set_points(Vector<Point> &p_points); + void set_points(const Vector<Point> &p_points); Vector<Point> &get_points(); void reverse(); diff --git a/scene/resources/immediate_mesh.cpp b/scene/resources/immediate_mesh.cpp index 28afef8638..044477e744 100644 --- a/scene/resources/immediate_mesh.cpp +++ b/scene/resources/immediate_mesh.cpp @@ -211,7 +211,7 @@ void ImmediateMesh::surface_end() { value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; if (t.d > 0) { - value |= 3 << 30; + value |= 3UL << 30; } *tangent = value; diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 30deb5ccd5..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++) { @@ -419,7 +419,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli continue; } - if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) { + if (new_index_count == 0 || (new_index_count >= (index_count * 0.75f))) { break; } @@ -521,7 +521,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli Vector3 normal = n0 * w + n1 * u + n2 * v; Vector2 orig_uv = ray_uvs[j]; - real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; + const real_t orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y }; for (int k = 0; k < 3; k++) { int idx = orig_tri_id * 3 + k; real_t weight = orig_bary[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++) { @@ -1023,7 +1023,7 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, // Keep only the scale Basis basis = p_base_transform.get_basis(); - Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length()); Transform3D transform; transform.scale(scale); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 430626b008..8abdfe713f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -84,7 +84,7 @@ void Material::inspect_native_shader_code() { SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()); RID shader = get_shader_rid(); if (st && shader.is_valid()) { - st->call_group("_native_shader_source_visualizer", "_inspect_shader", shader); + st->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_native_shader_source_visualizer", "_inspect_shader", shader); } } @@ -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() { @@ -395,6 +395,9 @@ void BaseMaterial3D::init_shaders() { shader_names->distance_fade_min = "distance_fade_min"; shader_names->distance_fade_max = "distance_fade_max"; + shader_names->msdf_pixel_range = "msdf_pixel_range"; + shader_names->msdf_outline_size = "msdf_outline_size"; + shader_names->metallic_texture_channel = "metallic_texture_channel"; shader_names->ao_texture_channel = "ao_texture_channel"; shader_names->clearcoat_texture_channel = "clearcoat_texture_channel"; @@ -432,12 +435,10 @@ void BaseMaterial3D::init_shaders() { shader_names->albedo_texture_size = "albedo_texture_size"; } -Ref<StandardMaterial3D> BaseMaterial3D::materials_for_2d[BaseMaterial3D::MAX_MATERIALS_FOR_2D]; +HashMap<uint64_t, Ref<StandardMaterial3D>> BaseMaterial3D::materials_for_2d; void BaseMaterial3D::finish_shaders() { - for (int i = 0; i < MAX_MATERIALS_FOR_2D; i++) { - materials_for_2d[i].unref(); - } + materials_for_2d.clear(); memdelete(dirty_materials); dirty_materials = nullptr; @@ -644,6 +645,11 @@ void BaseMaterial3D::_update_shader() { code += "uniform float distance_fade_max;\n"; } + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += "uniform float msdf_pixel_range;\n"; + code += "uniform float msdf_outline_size;\n"; + } + // alpha scissor is only valid if there is not antialiasing edge // alpha hash is valid whenever, but not with alpha scissor if (transparency == TRANSPARENCY_ALPHA_SCISSOR) { @@ -911,6 +917,12 @@ void BaseMaterial3D::_update_shader() { code += "}\n"; code += "\n\n"; + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += "float msdf_median(float r, float g, float b, float a) {\n"; + code += " return min(max(min(r, g), min(max(r, g), b)), a);\n"; + code += "}\n"; + } + code += "\n\n"; if (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR]) { code += "vec4 triplanar_texture(sampler2D p_sampler,vec3 p_weights,vec3 p_triplanar_pos) {\n"; code += " vec4 samp=vec4(0.0);\n"; @@ -1010,7 +1022,30 @@ void BaseMaterial3D::_update_shader() { } } - if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) { + if (flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + code += " {\n"; + code += " albedo_tex.rgb = mix(vec3(1.0 + 0.055) * pow(albedo_tex.rgb, vec3(1.0 / 2.4)) - vec3(0.055), vec3(12.92) * albedo_tex.rgb.rgb, lessThan(albedo_tex.rgb, vec3(0.0031308)));\n"; + code += " vec2 msdf_size = vec2(msdf_pixel_range) / vec2(textureSize(texture_albedo, 0));\n"; + if (flags[FLAG_USE_POINT_SIZE]) { + code += " vec2 dest_size = vec2(1.0) / fwidth(POINT_COORD);\n"; + } else { + if (flags[FLAG_UV1_USE_TRIPLANAR]) { + code += " vec2 dest_size = vec2(1.0) / fwidth(uv1_triplanar_pos);\n"; + } else { + code += " vec2 dest_size = vec2(1.0) / fwidth(base_uv);\n"; + } + } + code += " float px_size = max(0.5 * dot(msdf_size, dest_size), 1.0);\n"; + code += " float d = msdf_median(albedo_tex.r, albedo_tex.g, albedo_tex.b, albedo_tex.a) - 0.5;\n"; + code += " if (msdf_outline_size > 0.0) {\n"; + code += " float cr = clamp(msdf_outline_size, 0.0, msdf_pixel_range / 2.0) / msdf_pixel_range;\n"; + code += " albedo_tex.a = clamp((d + cr) * px_size, 0.0, 1.0);\n"; + code += " } else {\n"; + code += " albedo_tex.a = clamp(d * px_size + 0.5, 0.0, 1.0);\n"; + code += " }\n"; + code += " albedo_tex.rgb = vec3(1.0);\n"; + code += " }\n"; + } else if (flags[FLAG_ALBEDO_TEXTURE_FORCE_SRGB]) { code += " albedo_tex.rgb = mix(pow((albedo_tex.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)),vec3(2.4)),albedo_tex.rgb.rgb * (1.0 / 12.92),lessThan(albedo_tex.rgb,vec3(0.04045)));\n"; } @@ -1777,6 +1812,14 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NO_EDITOR; } + if (property.name == "msdf_pixel_range" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + + if (property.name == "msdf_outline_size" && !flags[FLAG_ALBEDO_TEXTURE_MSDF]) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + if ((property.name == "distance_fade_max_distance" || property.name == "distance_fade_min_distance") && distance_fade == DISTANCE_FADE_DISABLED) { property.usage = PROPERTY_USAGE_NO_EDITOR; } @@ -2125,35 +2168,45 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, RID *r_shader_rid) { - int version = 0; +Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, RID *r_shader_rid) { + int64_t hash = 0; if (p_shaded) { - version = 1; + hash |= 1 << 0; } if (p_transparent) { - version |= 2; + hash |= 1 << 1; } if (p_cut_alpha) { - version |= 4; + hash |= 1 << 2; } if (p_opaque_prepass) { - version |= 8; + hash |= 1 << 3; } if (p_double_sided) { - version |= 16; + hash |= 1 << 4; } if (p_billboard) { - version |= 32; + hash |= 1 << 5; } if (p_billboard_y) { - version |= 64; + hash |= 1 << 6; + } + if (p_msdf) { + hash |= 1 << 7; + } + if (p_no_depth) { + hash |= 1 << 8; + } + if (p_fixed_size) { + hash |= 1 << 9; } + hash = hash_djb2_one_64(p_filter, hash); - if (materials_for_2d[version].is_valid()) { + if (materials_for_2d.has(hash)) { if (r_shader_rid) { - *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); } - return materials_for_2d[version]; + return materials_for_2d[hash]; } Ref<StandardMaterial3D> material; @@ -2164,18 +2217,22 @@ Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, bool p_transpar material->set_cull_mode(p_double_sided ? CULL_DISABLED : CULL_BACK); material->set_flag(FLAG_SRGB_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(FLAG_ALBEDO_TEXTURE_MSDF, p_msdf); + material->set_flag(FLAG_DISABLE_DEPTH_TEST, p_no_depth); + material->set_flag(FLAG_FIXED_SIZE, p_fixed_size); + material->set_texture_filter(p_filter); if (p_billboard || p_billboard_y) { material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true); material->set_billboard_mode(p_billboard_y ? BILLBOARD_FIXED_Y : BILLBOARD_ENABLED); } - materials_for_2d[version] = material; + materials_for_2d[hash] = material; if (r_shader_rid) { - *r_shader_rid = materials_for_2d[version]->get_shader_rid(); + *r_shader_rid = materials_for_2d[hash]->get_shader_rid(); } - return materials_for_2d[version]; + return materials_for_2d[hash]; } void BaseMaterial3D::set_on_top_of_alpha() { @@ -2203,6 +2260,24 @@ float BaseMaterial3D::get_proximity_fade_distance() const { return proximity_fade_distance; } +void BaseMaterial3D::set_msdf_pixel_range(float p_range) { + msdf_pixel_range = p_range; + RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_pixel_range, p_range); +} + +float BaseMaterial3D::get_msdf_pixel_range() const { + return msdf_pixel_range; +} + +void BaseMaterial3D::set_msdf_outline_size(float p_size) { + msdf_outline_size = p_size; + RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_outline_size, p_size); +} + +float BaseMaterial3D::get_msdf_outline_size() const { + return msdf_outline_size; +} + void BaseMaterial3D::set_distance_fade(DistanceFadeMode p_mode) { distance_fade = p_mode; _queue_shader_change(); @@ -2445,6 +2520,12 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_proximity_fade_distance", "distance"), &BaseMaterial3D::set_proximity_fade_distance); ClassDB::bind_method(D_METHOD("get_proximity_fade_distance"), &BaseMaterial3D::get_proximity_fade_distance); + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "range"), &BaseMaterial3D::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &BaseMaterial3D::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_outline_size", "size"), &BaseMaterial3D::set_msdf_outline_size); + ClassDB::bind_method(D_METHOD("get_msdf_outline_size"), &BaseMaterial3D::get_msdf_outline_size); + ClassDB::bind_method(D_METHOD("set_distance_fade", "mode"), &BaseMaterial3D::set_distance_fade); ClassDB::bind_method(D_METHOD("get_distance_fade"), &BaseMaterial3D::get_distance_fade); @@ -2479,6 +2560,7 @@ void BaseMaterial3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "albedo_color"), "set_albedo", "get_albedo"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "albedo_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ALBEDO); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "albedo_tex_msdf"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_MSDF); ADD_GROUP("ORM", "orm_"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orm_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_ORM); @@ -2616,6 +2698,9 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Proximity Fade", "proximity_fade_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enable"), "set_proximity_fade", "is_proximity_fade_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_proximity_fade_distance", "get_proximity_fade_distance"); + ADD_GROUP("MSDF", "msdf_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "1,250,1"), "set_msdf_outline_size", "get_msdf_outline_size"); ADD_GROUP("Distance Fade", "distance_fade_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "distance_fade_mode", PROPERTY_HINT_ENUM, "Disabled,PixelAlpha,PixelDither,ObjectDither"), "set_distance_fade", "get_distance_fade"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance_fade_min_distance", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_distance_fade_min_distance", "get_distance_fade_min_distance"); @@ -2715,6 +2800,7 @@ void BaseMaterial3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_INVERT_HEIGHTMAP); BIND_ENUM_CONSTANT(FLAG_SUBSURFACE_MODE_SKIN); BIND_ENUM_CONSTANT(FLAG_PARTICLE_TRAILS_MODE); + BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_MSDF); BIND_ENUM_CONSTANT(FLAG_MAX); BIND_ENUM_CONSTANT(DIFFUSE_BURLEY); @@ -2804,6 +2890,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_heightmap_deep_parallax_max_layers(32); set_heightmap_deep_parallax_flip_tangent(false); //also sets binormal + flags[FLAG_ALBEDO_TEXTURE_MSDF] = false; flags[FLAG_USE_TEXTURE_REPEAT] = true; is_initialized = true; @@ -2923,4 +3010,7 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) return false; } + #endif // DISABLE_DEPRECATED + +/////////////////////// diff --git a/scene/resources/material.h b/scene/resources/material.h index 07227c037c..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); @@ -242,6 +242,7 @@ public: FLAG_INVERT_HEIGHTMAP, FLAG_SUBSURFACE_MODE_SKIN, FLAG_PARTICLE_TRAILS_MODE, + FLAG_ALBEDO_TEXTURE_MSDF, FLAG_MAX }; @@ -322,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; } @@ -336,7 +340,7 @@ private: int users = 0; }; - static Map<MaterialKey, ShaderData> shader_map; + static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map; MaterialKey current_key; @@ -412,6 +416,8 @@ private: StringName uv2_blend_sharpness; StringName grow; StringName proximity_fade_distance; + StringName msdf_pixel_range; + StringName msdf_outline_size; StringName distance_fade_min; StringName distance_fade_max; StringName ao_light_affect; @@ -446,36 +452,36 @@ private: bool orm; Color albedo; - float specular; - float metallic; - float roughness; + float specular = 0.0f; + float metallic = 0.0f; + float roughness = 0.0f; Color emission; - float emission_energy; - float normal_scale; - float rim; - float rim_tint; - float clearcoat; - float clearcoat_roughness; - float anisotropy; - float heightmap_scale; - float subsurface_scattering_strength; - float transmittance_amount; + float emission_energy = 0.0f; + float normal_scale = 0.0f; + float rim = 0.0f; + float rim_tint = 0.0f; + float clearcoat = 0.0f; + float clearcoat_roughness = 0.0f; + float anisotropy = 0.0f; + float heightmap_scale = 0.0f; + float subsurface_scattering_strength = 0.0f; + float transmittance_amount = 0.0f; Color transmittance_color; - float transmittance_depth; - float transmittance_boost; + float transmittance_depth = 0.0f; + float transmittance_boost = 0.0f; Color backlight; - float refraction; - float point_size; - float alpha_scissor_threshold; - float alpha_hash_scale; - float alpha_antialiasing_edge; + float refraction = 0.0f; + float point_size = 0.0f; + float alpha_scissor_threshold = 0.0f; + float alpha_hash_scale = 0.0f; + float alpha_antialiasing_edge = 0.0f; bool grow_enabled = false; - float ao_light_affect; - float grow; - int particles_anim_h_frames; - int particles_anim_v_frames; - bool particles_anim_loop; + float ao_light_affect = 0.0f; + float grow = 0.0f; + int particles_anim_h_frames = 0; + int particles_anim_v_frames = 0; + bool particles_anim_loop = false; Transparency transparency = TRANSPARENCY_DISABLED; ShadingMode shading_mode = SHADING_MODE_PER_PIXEL; @@ -483,26 +489,29 @@ private: Vector3 uv1_scale; Vector3 uv1_offset; - float uv1_triplanar_sharpness; + float uv1_triplanar_sharpness = 0.0f; Vector3 uv2_scale; Vector3 uv2_offset; - float uv2_triplanar_sharpness; + float uv2_triplanar_sharpness = 0.0f; DetailUV detail_uv = DETAIL_UV_1; bool deep_parallax = false; - int deep_parallax_min_layers; - int deep_parallax_max_layers; + int deep_parallax_min_layers = 0; + int deep_parallax_max_layers = 0; bool heightmap_parallax_flip_tangent = false; bool heightmap_parallax_flip_binormal = false; bool proximity_fade_enabled = false; - float proximity_fade_distance; + float proximity_fade_distance = 0.0f; + + float msdf_pixel_range = 4.f; + float msdf_outline_size = 0.f; DistanceFadeMode distance_fade = DISTANCE_FADE_DISABLED; - float distance_fade_max_distance; - float distance_fade_min_distance; + float distance_fade_max_distance = 0.0f; + float distance_fade_min_distance = 0.0f; BlendMode blend_mode = BLEND_MODE_MIX; BlendMode detail_blend_mode = BLEND_MODE_MIX; @@ -527,9 +536,7 @@ private: _FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const; - static const int MAX_MATERIALS_FOR_2D = 128; - - static Ref<StandardMaterial3D> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff + static HashMap<uint64_t, Ref<StandardMaterial3D>> materials_for_2d; //used by Sprite3D, Label3D and other stuff void _validate_high_end(const String &text, PropertyInfo &property) const; @@ -714,6 +721,12 @@ public: void set_proximity_fade_distance(float p_distance); float get_proximity_fade_distance() const; + void set_msdf_pixel_range(float p_range); + float get_msdf_pixel_range() const; + + void set_msdf_outline_size(float p_size); + float get_msdf_outline_size() const; + void set_distance_fade(DistanceFadeMode p_mode); DistanceFadeMode get_distance_fade() const; @@ -739,7 +752,7 @@ public: static void finish_shaders(); static void flush_changes(); - static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, RID *r_shader_rid = nullptr); + static Ref<Material> get_material_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; @@ -787,6 +800,13 @@ public: BaseMaterial3D(true) {} }; +class PlaceholderMaterial : public Material { + GDCLASS(PlaceholderMaterial, Material) +public: + virtual RID get_shader_rid() const override { return RID(); } + virtual Shader::Mode get_shader_mode() const override { return Shader::MODE_CANVAS_ITEM; } +}; + ////////////////////// #endif diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 3c67a20f50..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; } @@ -1827,7 +1827,7 @@ Error ArrayMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, flo // Keep only the scale Basis basis = p_base_transform.get_basis(); - Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length()); + Vector3 scale = Vector3(basis.get_column(0).length(), basis.get_column(1).length(), basis.get_column(2).length()); Transform3D transform; transform.scale(scale); @@ -2092,3 +2092,17 @@ ArrayMesh::~ArrayMesh() { RenderingServer::get_singleton()->free(mesh); } } +/////////////// + +void PlaceholderMesh::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aabb", "aabb"), &PlaceholderMesh::set_aabb); + ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb", PROPERTY_HINT_NONE, ""), "set_aabb", "get_aabb"); +} + +PlaceholderMesh::PlaceholderMesh() { + rid = RS::get_singleton()->mesh_create(); +} + +PlaceholderMesh::~PlaceholderMesh() { + RS::get_singleton()->free(rid); +} diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 652c045a24..b166d12ead 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -328,4 +328,38 @@ VARIANT_ENUM_CAST(Mesh::ArrayCustomFormat); VARIANT_ENUM_CAST(Mesh::PrimitiveType); VARIANT_ENUM_CAST(Mesh::BlendShapeMode); +class PlaceholderMesh : public Mesh { + GDCLASS(PlaceholderMesh, Mesh); + + RID rid; + AABB aabb; + +protected: + static void _bind_methods(); + +public: + virtual int get_surface_count() const override { return 0; } + virtual int surface_get_array_len(int p_idx) const override { return 0; } + virtual int surface_get_array_index_len(int p_idx) const override { return 0; } + virtual Array surface_get_arrays(int p_surface) const override { return Array(); } + virtual Array surface_get_blend_shape_arrays(int p_surface) const override { return Array(); } + virtual Dictionary surface_get_lods(int p_surface) const override { return Dictionary(); } + virtual uint32_t surface_get_format(int p_idx) const override { return 0; } + virtual PrimitiveType surface_get_primitive_type(int p_idx) const override { return PRIMITIVE_TRIANGLES; } + virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override {} + virtual Ref<Material> surface_get_material(int p_idx) const override { return Ref<Material>(); } + virtual int get_blend_shape_count() const override { return 0; } + virtual StringName get_blend_shape_name(int p_index) const override { return StringName(); } + virtual void set_blend_shape_name(int p_index, const StringName &p_name) override {} + virtual RID get_rid() const override { return rid; } + virtual AABB get_aabb() const override { return aabb; } + void set_aabb(const AABB &p_aabb) { aabb = p_aabb; } + + virtual int get_builtin_bind_pose_count() const override { return 0; } + virtual Transform3D get_builtin_bind_pose(int p_index) const override { return Transform3D(); } + + PlaceholderMesh(); + ~PlaceholderMesh(); +}; + #endif 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.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/multimesh.cpp b/scene/resources/multimesh.cpp index c30e748f66..e5fc61ade5 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -101,9 +101,9 @@ void MultiMesh::_set_transform_2d_array(const Vector<Vector2> &p_array) { for (int i = 0; i < len / 3; i++) { Transform2D t; - t.elements[0] = r[i * 3 + 0]; - t.elements[1] = r[i * 3 + 1]; - t.elements[2] = r[i * 3 + 2]; + t.columns[0] = r[i * 3 + 0]; + t.columns[1] = r[i * 3 + 1]; + t.columns[2] = r[i * 3 + 2]; set_instance_transform_2d(i, t); } @@ -125,9 +125,9 @@ Vector<Vector2> MultiMesh::_get_transform_2d_array() const { for (int i = 0; i < instance_count; i++) { Transform2D t = get_instance_transform_2d(i); - w[i * 3 + 0] = t.elements[0]; - w[i * 3 + 1] = t.elements[1]; - w[i * 3 + 2] = t.elements[2]; + w[i * 3 + 0] = t.columns[0]; + w[i * 3 + 1] = t.columns[1]; + w[i * 3 + 2] = t.columns[2]; } return xforms; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 47a87bdea5..b1270e9dd6 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -338,7 +338,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 +356,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; @@ -388,6 +388,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() { debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + if (!lines.size()) { + return debug_mesh; + } + Array arr; arr.resize(Mesh::ARRAY_MAX); arr[Mesh::ARRAY_VERTEX] = varr; 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 b1c2702a1e..c7b1981aed 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -33,11 +33,13 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/core_string_names.h" +#include "core/io/missing_resource.h" #include "core/io/resource_loader.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/property_utils.h" #define PACKED_SCENE_VERSION 2 @@ -47,14 +49,11 @@ bool SceneState::can_instantiate() const { } static Array _sanitize_node_pinned_properties(Node *p_node) { - if (!p_node->has_meta("_edit_pinned_properties_")) { - return Array(); - } - Array pinned = p_node->get_meta("_edit_pinned_properties_"); + Array pinned = p_node->get_meta("_edit_pinned_properties_", Array()); 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 { @@ -107,12 +106,13 @@ 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]; Node *parent = nullptr; + String old_parent_path; if (i > 0) { ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])); @@ -120,6 +120,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); + nparent = ret_nodes[0]; } #endif parent = nparent; @@ -130,6 +132,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } Node *node = nullptr; + MissingNode *missing_node = nullptr; if (i == 0 && base_scene_idx >= 0) { //scene inheritance on root node @@ -184,24 +187,33 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { memdelete(obj); obj = nullptr; } - WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); - if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { - if (Object::cast_to<Control>(ret_nodes[n.parent])) { - obj = memnew(Control); - } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { - obj = memnew(Node2D); + + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_node = memnew(MissingNode); + missing_node->set_original_class(snames[n.type]); + missing_node->set_recording_properties(true); + node = missing_node; + obj = missing_node; + } else { + WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); + if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { + if (Object::cast_to<Control>(ret_nodes[n.parent])) { + obj = memnew(Control); + } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { + obj = memnew(Node2D); #ifndef _3D_DISABLED - } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { - obj = memnew(Node3D); + } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { + obj = memnew(Node3D); #endif // _3D_DISABLED + } } - } - if (!obj) { - obj = memnew(Node); - } + if (!obj) { + obj = memnew(Node); + } - node = Object::cast_to<Node>(obj); + node = Object::cast_to<Node>(obj); + } } } @@ -214,6 +226,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (nprop_count) { const NodeData::Property *nprops = &n.properties[0]; + Dictionary missing_resource_properties; + for (int j = 0; j < nprop_count; j++) { bool valid; ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); @@ -243,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]; @@ -270,9 +284,24 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } - node->set(snames[nprops[j].name], value, &valid); + + bool set_valid = true; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled() && value.get_type() == Variant::OBJECT) { + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[snames[nprops[j].name]] = mr; + set_valid = false; + } + } + + if (set_valid) { + node->set(snames[nprops[j].name], value, &valid); + } } } + if (!missing_resource_properties.is_empty()) { + node->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } } //name @@ -306,10 +335,17 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (!old_parent_path.is_empty()) { + node->_set_name_nocheck(old_parent_path + "@" + node->get_name()); + } + if (n.owner >= 0) { NODE_FROM_ID(owner, n.owner); if (owner) { node->_set_owner_nocheck(owner); + if (node->data.unique_name_in_owner) { + node->_acquire_unique_name_in_owner(); + } } } @@ -321,6 +357,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (missing_node) { + missing_node->set_recording_properties(false); + } + ret_nodes[i] = node; if (node && gen_node_path_cache && ret_nodes[0]) { @@ -390,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]; } @@ -410,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 @@ -482,33 +522,36 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map p_node->get_property_list(&plist); Array pinned_props = _sanitize_node_pinned_properties(p_node); + Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary()); for (const PropertyInfo &E : plist) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant forced_value; + if (E.name == META_PROPERTY_MISSING_RESOURCES) { + continue; // Ignore this property when packing. + } - // If instance or inheriting, not saving if property requested so, or it's meta - if (states_stack.size()) { + // If instance or inheriting, not saving if property requested so. + if (!states_stack.is_empty()) { if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { continue; } - // Meta is normally not saved in instances/inherited (see GH-12838), but we need to save the pinned list - if (E.name == "__meta__") { - if (pinned_props.size()) { - Dictionary meta_override; - meta_override["_edit_pinned_properties_"] = pinned_props; - forced_value = meta_override; - } - } } StringName name = E.name; - Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value; + Variant value = p_node->get(name); - if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) { + if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) { + // Was this missing resource overriden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[E.name]; + } + } + + if (!pinned_props.has(name)) { bool is_valid_default = false; Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true); if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) { @@ -563,11 +606,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.owner = -1; } + MissingNode *missing_node = Object::cast_to<MissingNode>(p_node); + // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type - nd.type = _nm_get_string(p_node->get_class(), name_map); + if (missing_node != nullptr) { + // Its a missing node (type non existant on load). + nd.type = _nm_get_string(missing_node->get_original_class(), name_map); + } else { + nd.type = _nm_get_string(p_node->get_class(), name_map); + } } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated @@ -622,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; } @@ -829,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()) { @@ -863,10 +913,10 @@ Error SceneState::pack(Node *p_scene) { } variants.resize(variant_map.size()); - const Variant *K = nullptr; - while ((K = variant_map.next(K))) { - int idx = variant_map[*K]; - variants.write[idx] = *K; + + for (const KeyValue<Variant, int> &E : variant_map) { + int idx = E.value; + variants.write[idx] = E.key; } node_paths.resize(nodepath_map.size()); 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 01a0411545..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() { @@ -233,48 +233,48 @@ void ParticlesMaterial::_update_shader() { code += "uniform vec3 gravity;\n"; if (color_ramp.is_valid()) { - code += "uniform sampler2D color_ramp;\n"; + code += "uniform sampler2D color_ramp : repeat_disable;\n"; } if (color_initial_ramp.is_valid()) { - code += "uniform sampler2D color_initial_ramp;\n"; + code += "uniform sampler2D color_initial_ramp : repeat_disable;\n"; } if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - code += "uniform sampler2D linear_velocity_texture;\n"; + code += "uniform sampler2D linear_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - code += "uniform sampler2D orbit_velocity_texture;\n"; + code += "uniform sampler2D orbit_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - code += "uniform sampler2D angular_velocity_texture;\n"; + code += "uniform sampler2D angular_velocity_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - code += "uniform sampler2D linear_accel_texture;\n"; + code += "uniform sampler2D linear_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - code += "uniform sampler2D radial_accel_texture;\n"; + code += "uniform sampler2D radial_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - code += "uniform sampler2D tangent_accel_texture;\n"; + code += "uniform sampler2D tangent_accel_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_DAMPING].is_valid()) { - code += "uniform sampler2D damping_texture;\n"; + code += "uniform sampler2D damping_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANGLE].is_valid()) { - code += "uniform sampler2D angle_texture;\n"; + code += "uniform sampler2D angle_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_SCALE].is_valid()) { - code += "uniform sampler2D scale_texture;\n"; + code += "uniform sampler2D scale_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_HUE_VARIATION].is_valid()) { - code += "uniform sampler2D hue_variation_texture;\n"; + code += "uniform sampler2D hue_variation_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANIM_SPEED].is_valid()) { - code += "uniform sampler2D anim_speed_texture;\n"; + code += "uniform sampler2D anim_speed_texture : repeat_disable;\n"; } if (tex_parameters[PARAM_ANIM_OFFSET].is_valid()) { - code += "uniform sampler2D anim_offset_texture;\n"; + code += "uniform sampler2D anim_offset_texture : repeat_disable;\n"; } if (collision_enabled) { diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 57da344ce0..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; @@ -230,8 +238,8 @@ private: bool is_initialized = false; Vector3 direction; - float spread; - float flatness; + float spread = 0.0f; + float flatness = 0.0f; float params_min[PARAM_MAX]; float params_max[PARAM_MAX]; @@ -244,34 +252,34 @@ private: bool particle_flags[PARTICLE_FLAG_MAX]; EmissionShape emission_shape; - float emission_sphere_radius; + float emission_sphere_radius = 0.0f; Vector3 emission_box_extents; Ref<Texture2D> emission_point_texture; Ref<Texture2D> emission_normal_texture; Ref<Texture2D> emission_color_texture; Vector3 emission_ring_axis; - real_t emission_ring_height; - real_t emission_ring_radius; - real_t emission_ring_inner_radius; + real_t emission_ring_height = 0.0f; + real_t emission_ring_radius = 0.0f; + real_t emission_ring_inner_radius = 0.0f; int emission_point_count = 1; - bool anim_loop; + bool anim_loop = false; Vector3 gravity; - double lifetime_randomness; + double lifetime_randomness = 0.0; SubEmitterMode sub_emitter_mode; - double sub_emitter_frequency; - int sub_emitter_amount_at_end; - bool sub_emitter_keep_velocity; + double sub_emitter_frequency = 0.0; + int sub_emitter_amount_at_end = 0; + bool sub_emitter_keep_velocity = false; //do not save emission points here - bool attractor_interaction_enabled; - bool collision_enabled; - bool collision_scale; - float collision_friction; - float collision_bounce; + bool attractor_interaction_enabled = false; + bool collision_enabled = false; + bool collision_scale = false; + float collision_friction = 0.0f; + float collision_bounce = 0.0f; protected: static void _bind_methods(); diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 882afdb43d..155a8522cf 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -34,7 +34,7 @@ bool PolygonPathFinder::_is_point_inside(const Vector2 &p_point) const { int crosses = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); Vector2 a = points[e.points[0]].pos; @@ -105,7 +105,7 @@ 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()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); if (e.points[0] == i || e.points[1] == i || e.points[0] == j || e.points[1] == j) { continue; @@ -137,10 +137,10 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector Edge ignore_to_edge(-1, -1); if (!_is_point_inside(from)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); Vector2 seg[2] = { points[e.points[0]].pos, @@ -161,10 +161,10 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector }; if (!_is_point_inside(to)) { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); Vector2 seg[2] = { points[e.points[0]].pos, @@ -188,7 +188,7 @@ 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()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); if (e.points[0] == ignore_from_edge.points[0] && e.points[1] == ignore_from_edge.points[1]) { continue; @@ -240,7 +240,7 @@ 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()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); if (e.points[0] == i || e.points[1] == i) { @@ -289,11 +289,11 @@ 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()) { + for (RBSet<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; @@ -312,7 +312,7 @@ 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()) { + for (RBSet<int>::Element *E = open_list.front(); E; E = E->next()) { const Point &p = points[E->get()]; float cost = p.distance; cost += p.pos.distance_to(to); @@ -327,7 +327,7 @@ 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()) { + for (RBSet<int>::Element *E = np.connections.front(); E; E = E->next()) { Point &p = points.write[E->get()]; float distance = np.pos.distance_to(p.pos) + np.distance; @@ -459,7 +459,7 @@ 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()) { + for (RBSet<int>::Element *E = points[i].connections.front(); E; E = E->next()) { cw[idx++] = E->get(); } } @@ -469,7 +469,7 @@ Dictionary PolygonPathFinder::_get_data() const { { int *iw = ind.ptrw(); int idx = 0; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { iw[idx++] = E->get().points[0]; iw[idx++] = E->get().points[1]; } @@ -489,10 +489,10 @@ bool PolygonPathFinder::is_point_inside(const Vector2 &p_point) const { } Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { - float closest_dist = 1e20; + float closest_dist = 1e20f; Vector2 closest_point; - for (Set<Edge>::Element *E = edges.front(); E; E = E->next()) { + for (RBSet<Edge>::Element *E = edges.front(); E; E = E->next()) { const Edge &e = E->get(); Vector2 seg[2] = { points[e.points[0]].pos, @@ -508,7 +508,7 @@ Vector2 PolygonPathFinder::get_closest_point(const Vector2 &p_point) const { } } - ERR_FAIL_COND_V(closest_dist == 1e20, Vector2()); + ERR_FAIL_COND_V(Math::is_equal_approx(closest_dist, 1e20f), Vector2()); return closest_point; } @@ -516,7 +516,7 @@ 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()) { + for (RBSet<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; 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 781e219f1f..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 @@ -270,6 +275,10 @@ PrimitiveMesh::~PrimitiveMesh() { */ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, radius, height, radial_segments, rings); +} + +void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, w; float onethird = 1.0 / 3.0; @@ -310,7 +319,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, 0.5 * height - radius, 0.0)); normals.push_back(p.normalized()); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * onethird)); point++; @@ -349,7 +358,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius, y, -z * radius); points.push_back(p); normals.push_back(Vector3(x, 0.0, -z)); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, onethird + (v * onethird))); point++; @@ -389,7 +398,7 @@ void CapsuleMesh::_create_mesh_array(Array &p_arr) const { Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0)); normals.push_back(p.normalized()); - ADD_TANGENT(z, 0.0, x, 1.0) + ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird))); point++; @@ -481,6 +490,10 @@ CapsuleMesh::CapsuleMesh() {} */ void BoxMesh::_create_mesh_array(Array &p_arr) const { + BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d); +} + +void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; @@ -732,6 +745,10 @@ BoxMesh::BoxMesh() {} */ void CylinderMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings); +} + +void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, radius; @@ -1431,6 +1448,10 @@ Vector3 QuadMesh::get_center_offset() const { */ void SphereMesh::_create_mesh_array(Array &p_arr) const { + create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere); +} + +void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) { int i, j, prevrow, thisrow, point; float x, y, z; @@ -2135,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 eef5eb3f7d..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... @@ -117,6 +119,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8); + void set_radius(const float p_radius); float get_radius() const; @@ -149,6 +153,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0); + void set_size(const Vector3 &p_size); Vector3 get_size() const; @@ -183,6 +189,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4); + void set_top_radius(const float p_radius); float get_top_radius() const; @@ -241,7 +249,7 @@ class PrismMesh : public PrimitiveMesh { private: float left_to_right = 0.5; - Vector3 size = Vector3(2.0, 2.0, 2.0); + Vector3 size = Vector3(1.0, 1.0, 1.0); int subdivide_w = 0; int subdivide_h = 0; int subdivide_d = 0; @@ -303,8 +311,8 @@ class SphereMesh : public PrimitiveMesh { GDCLASS(SphereMesh, PrimitiveMesh); private: - float radius = 1.0; - float height = 2.0; + float radius = 0.5; + float height = 1.0; int radial_segments = 64; int rings = 32; bool is_hemisphere = false; @@ -314,6 +322,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false); + void set_radius(const float p_radius); float get_radius() const; @@ -350,7 +360,7 @@ class TubeTrailMesh : public PrimitiveMesh { GDCLASS(TubeTrailMesh, PrimitiveMesh); private: - float radius = 1.0; + float radius = 0.5; int radial_steps = 8; int sections = 5; float section_length = 0.2; @@ -439,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 fd6f018651..193bd0ac05 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/dir_access.h" +#include "core/io/missing_resource.h" #include "core/io/resource_format_binary.h" #include "core/version.h" @@ -150,7 +151,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R if (ext_resources[id].cache.is_valid()) { r_res = ext_resources[id].cache; } else if (use_sub_threads) { - RES res = ResourceLoader::load_threaded_get(path); + Ref<Resource> res = ResourceLoader::load_threaded_get(path); if (res.is_null()) { if (ResourceLoader::get_abort_on_missing_resources()) { error = ERR_FILE_MISSING_DEPENDENCIES; @@ -171,7 +172,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R return error; } } else { - r_res = RES(); + r_res = Ref<Resource>(); } VariantParser::get_token(p_stream, token, line, r_err_str); @@ -460,7 +461,7 @@ Error ResourceLoaderText::load() { } } else { - RES res = ResourceLoader::load(path, type); + Ref<Resource> res = ResourceLoader::load(path, type); if (res.is_null()) { if (ResourceLoader::get_abort_on_missing_resources()) { @@ -535,6 +536,8 @@ Error ResourceLoaderText::load() { } } + MissingResource *missing_resource = nullptr; + if (res.is_null()) { //not reuse if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE && ResourceCache::has(path)) { //only if it doesn't exist //cached, do not assign @@ -545,10 +548,17 @@ Error ResourceLoaderText::load() { Object *obj = ClassDB::instantiate(type); if (!obj) { - error_text += "Can't create sub resource of type: " + type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -572,6 +582,8 @@ Error ResourceLoaderText::load() { res->set_scene_unique_id(id); } + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -585,7 +597,23 @@ Error ResourceLoaderText::load() { if (!assign.is_empty()) { if (do_assign) { - res->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + res->set(assign, value); + } } //it's assignment } else if (!next_tag.name.is_empty()) { @@ -599,6 +627,14 @@ Error ResourceLoaderText::load() { } } + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + res->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + if (progress && resources_total > 0) { *progress = resource_current / float(resources_total); } @@ -624,13 +660,22 @@ Error ResourceLoaderText::load() { } } + MissingResource *missing_resource = nullptr; + if (!resource.is_valid()) { Object *obj = ClassDB::instantiate(res_type); if (!obj) { - error_text += "Can't create sub resource of type: " + res_type; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_resource = memnew(MissingResource); + missing_resource->set_original_class(res_type); + missing_resource->set_recording_properties(true); + obj = missing_resource; + } else { + error_text += "Can't create sub resource of type: " + res_type; + _printerr(); + error = ERR_FILE_CORRUPT; + return error; + } } Resource *r = Object::cast_to<Resource>(obj); @@ -646,6 +691,8 @@ Error ResourceLoaderText::load() { resource_current++; + Dictionary missing_resource_properties; + while (true) { String assign; Variant value; @@ -668,7 +715,23 @@ Error ResourceLoaderText::load() { } if (!assign.is_empty()) { - resource->set(assign, value); + bool set_valid = true; + + if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) { + // If the property being set is a missing resource (and the parent is not), + // then setting it will most likely not work. + // Instead, save it as metadata. + + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[assign] = mr; + set_valid = false; + } + } + + if (set_valid) { + resource->set(assign, value); + } //it's assignment } else if (!next_tag.name.is_empty()) { error = ERR_FILE_CORRUPT; @@ -676,14 +739,24 @@ Error ResourceLoaderText::load() { _printerr(); return error; } else { - error = OK; - if (progress && resources_total > 0) { - *progress = resource_current / float(resources_total); - } - - return error; + break; } } + + if (missing_resource) { + missing_resource->set_recording_properties(false); + } + + if (!missing_resource_properties.is_empty()) { + resource->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } + + error = OK; + if (progress && resources_total > 0) { + *progress = resource_current / float(resources_total); + } + + return error; } //for scene files @@ -738,11 +811,7 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) { ResourceLoaderText::ResourceLoaderText() {} -ResourceLoaderText::~ResourceLoaderText() { - memdelete(f); -} - -void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { +void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); ignore_resource_parsing = true; ERR_FAIL_COND(error != OK); @@ -798,13 +867,13 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen } } -Error ResourceLoaderText::rename_dependencies(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; //FileAccess - FileAccess *fw = nullptr; + Ref<FileAccess> fw; String base_path = local_path.get_base_dir(); @@ -814,23 +883,20 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p Error err = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp); if (err != OK) { - if (fw) { - memdelete(fw); - } error = ERR_FILE_CORRUPT; ERR_FAIL_V(error); } if (next_tag.name != "ext_resource") { //nothing was done - if (!fw) { + if (fw.is_null()) { return OK; } break; } else { - if (!fw) { + if (fw.is_null()) { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); if (is_scene) { fw->store_line("[gd_scene load_steps=" + itos(resources_total) + " format=" + itos(FORMAT_VERSION) + "]\n"); @@ -840,7 +906,6 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p } if (!next_tag.fields.has("path") || !next_tag.fields.has("id") || !next_tag.fields.has("type")) { - memdelete(fw); error = ERR_FILE_CORRUPT; ERR_FAIL_V(error); } @@ -898,24 +963,17 @@ Error ResourceLoaderText::rename_dependencies(FileAccess *p_f, const String &p_p fw->store_8(c); c = f->get_8(); } - f->close(); bool all_ok = fw->get_error() == OK; - memdelete(fw); - if (!all_ok) { return ERR_CANT_CREATE; } - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(p_path); - da->rename(p_path + ".depren", p_path); - return OK; } -void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { +void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) { error = OK; lines = 1; @@ -992,23 +1050,23 @@ void ResourceLoaderText::open(FileAccess *p_f, bool p_skip_first_tag) { rp.userdata = this; } -static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) { +static void bs_save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len = false) { CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32((utf8.length() + 1) | 0x80000000); + p_f->store_32((utf8.length() + 1) | 0x80000000); } else { - f->store_32(utf8.length() + 1); + p_f->store_32(utf8.length() + 1); } - f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); + p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } -Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) { +Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_path) { if (error) { return error; } - FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE); - if (!wf) { + Ref<FileAccess> wf = FileAccess::open(p_path, FileAccess::WRITE); + if (wf.is_null()) { return ERR_CANT_OPEN; } @@ -1023,7 +1081,7 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) static const int save_format_version = 3; //use format version 3 for saving wf->store_32(save_format_version); - bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type); + bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type); wf->store_64(0); //offset to import metadata, this is no longer used wf->store_32(ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS | ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); @@ -1078,8 +1136,8 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) uid = ResourceUID::get_singleton()->text_to_id(uidt); } - bs_save_unicode_string(wf.f, type); - bs_save_unicode_string(wf.f, path); + bs_save_unicode_string(wf, type); + bs_save_unicode_string(wf, path); wf->store_64(uid); int lindex = dummy_read.external_resources.size(); @@ -1108,146 +1166,145 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) wf->store_32(0); //zero sub resources, still parsing them String temp_file = p_path + ".temp"; - FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE); - if (!wf2) { - return ERR_CANT_OPEN; - } - Vector<uint64_t> local_offsets; Vector<uint64_t> local_pointers_pos; + { + Ref<FileAccess> wf2 = FileAccess::open(temp_file, FileAccess::WRITE); + if (wf2.is_null()) { + return ERR_CANT_OPEN; + } - while (next_tag.name == "sub_resource" || next_tag.name == "resource") { - String type; - int id = -1; - bool main_res; + while (next_tag.name == "sub_resource" || next_tag.name == "resource") { + String type; + int id = -1; + bool main_res; - if (next_tag.name == "sub_resource") { - if (!next_tag.fields.has("type")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'type' in external resource tag"; - _printerr(); - return error; - } + if (next_tag.name == "sub_resource") { + if (!next_tag.fields.has("type")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'type' in external resource tag"; + _printerr(); + return error; + } - if (!next_tag.fields.has("id")) { - error = ERR_FILE_CORRUPT; - error_text = "Missing 'id' in external resource tag"; - _printerr(); - return error; + if (!next_tag.fields.has("id")) { + error = ERR_FILE_CORRUPT; + error_text = "Missing 'id' in external resource tag"; + _printerr(); + return error; + } + + type = next_tag.fields["type"]; + id = next_tag.fields["id"]; + main_res = false; + } else { + type = res_type; + id = 0; //used for last anyway + main_res = true; } - type = next_tag.fields["type"]; - id = next_tag.fields["id"]; - main_res = false; - } else { - type = res_type; - id = 0; //used for last anyway - main_res = true; - } + local_offsets.push_back(wf2->get_position()); - local_offsets.push_back(wf2->get_position()); + bs_save_unicode_string(wf, "local://" + itos(id)); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset - bs_save_unicode_string(wf, "local://" + itos(id)); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset + bs_save_unicode_string(wf2, type); + uint64_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); - bs_save_unicode_string(wf2, type); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); + int prop_count = 0; - int prop_count = 0; + while (true) { + String assign; + Variant value; - while (true) { - String assign; - Variant value; + error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); - error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp); + if (error) { + if (main_res && error == ERR_FILE_EOF) { + next_tag.name = ""; //exit + break; + } - if (error) { - if (main_res && error == ERR_FILE_EOF) { - next_tag.name = ""; //exit - break; + _printerr(); + return error; } - _printerr(); - return error; + if (!assign.is_empty()) { + 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++; + + } else if (!next_tag.name.is_empty()) { + error = OK; + break; + } else { + error = ERR_FILE_CORRUPT; + error_text = "Premature end of file while parsing [sub_resource]"; + _printerr(); + return error; + } } - if (!assign.is_empty()) { - Map<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++; + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); + } - } else if (!next_tag.name.is_empty()) { - error = OK; - break; - } else { - error = ERR_FILE_CORRUPT; - error_text = "Premature end of file while parsing [sub_resource]"; + if (next_tag.name == "node") { + //this is a node, must save one more! + + if (!is_scene) { + error_text += "found the 'node' tag on a resource file!"; _printerr(); + error = ERR_FILE_CORRUPT; return error; } - } - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); - } - - if (next_tag.name == "node") { - //this is a node, must save one more! + Ref<PackedScene> packed_scene = _parse_node_tag(rp); - if (!is_scene) { - error_text += "found the 'node' tag on a resource file!"; - _printerr(); - error = ERR_FILE_CORRUPT; - return error; - } + if (!packed_scene.is_valid()) { + return error; + } - Ref<PackedScene> packed_scene = _parse_node_tag(rp); + error = OK; + //get it here + List<PropertyInfo> props; + packed_scene->get_property_list(&props); - if (!packed_scene.is_valid()) { - return error; - } + bs_save_unicode_string(wf, "local://0"); + local_pointers_pos.push_back(wf->get_position()); + wf->store_64(0); //temp local offset - error = OK; - //get it here - List<PropertyInfo> props; - packed_scene->get_property_list(&props); + local_offsets.push_back(wf2->get_position()); + bs_save_unicode_string(wf2, "PackedScene"); + uint64_t propcount_ofs = wf2->get_position(); + wf2->store_32(0); - bs_save_unicode_string(wf, "local://0"); - local_pointers_pos.push_back(wf->get_position()); - wf->store_64(0); //temp local offset + int prop_count = 0; - local_offsets.push_back(wf2->get_position()); - bs_save_unicode_string(wf2, "PackedScene"); - uint64_t propcount_ofs = wf2->get_position(); - wf2->store_32(0); + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { + continue; + } - int prop_count = 0; + String name = E.name; + Variant value = packed_scene->get(name); - for (const PropertyInfo &E : props) { - if (!(E.usage & PROPERTY_USAGE_STORAGE)) { - continue; + 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++; } - String name = E.name; - Variant value = packed_scene->get(name); - - Map<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++; + wf2->seek(propcount_ofs); + wf2->store_32(prop_count); + wf2->seek_end(); } - - wf2->seek(propcount_ofs); - wf2->store_32(prop_count); - wf2->seek_end(); } - wf2->close(); - uint64_t offset_from = wf->get_position(); wf->seek(sub_res_count_pos); //plus one because the saved one wf->store_32(local_offsets.size()); @@ -1262,18 +1319,16 @@ Error ResourceLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file); wf->store_buffer(data.ptr(), data.size()); { - DirAccessRef dar = DirAccess::open(temp_file.get_base_dir()); + Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir()); dar->remove(temp_file); } wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end - wf->close(); - return OK; } -String ResourceLoaderText::recognize(FileAccess *p_f) { +String ResourceLoaderText::recognize(Ref<FileAccess> p_f) { error = OK; lines = 1; @@ -1317,7 +1372,7 @@ String ResourceLoaderText::recognize(FileAccess *p_f) { return tag.fields["type"]; } -ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) { +ResourceUID::ID ResourceLoaderText::get_uid(Ref<FileAccess> p_f) { error = OK; lines = 1; @@ -1345,16 +1400,16 @@ ResourceUID::ID ResourceLoaderText::get_uid(FileAccess *p_f) { ///////////////////// -RES ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderText::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { if (r_error) { *r_error = ERR_CANT_OPEN; } Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'."); ResourceLoaderText loader; String path = !p_original_path.is_empty() ? p_original_path : p_path; @@ -1371,7 +1426,7 @@ RES ResourceFormatLoaderText::load(const String &p_path, const String &p_origina if (err == OK) { return loader.get_resource(); } else { - return RES(); + return Ref<Resource>(); } } @@ -1381,9 +1436,12 @@ void ResourceFormatLoaderText::get_recognized_extensions_for_type(const String & return; } - if (p_type == "PackedScene") { + if (ClassDB::is_parent_class("PackedScene", p_type)) { p_extensions->push_back("tscn"); - } else { + } + + // Don't allow .tres for PackedScenes. + if (p_type != "PackedScene") { p_extensions->push_back("tres"); } } @@ -1407,8 +1465,8 @@ String ResourceFormatLoaderText::get_resource_type(const String &p_path) const { // ...for anything else must test... - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ""; //could not read } @@ -1426,8 +1484,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) return ResourceUID::INVALID_ID; } - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ResourceUID::INVALID_ID; //could not read } @@ -1438,8 +1496,8 @@ ResourceUID::ID ResourceFormatLoaderText::get_resource_uid(const String &p_path) } void ResourceFormatLoaderText::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { ERR_FAIL(); } @@ -1449,23 +1507,34 @@ 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) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { - ERR_FAIL_V(ERR_CANT_OPEN); +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); + if (f.is_null()) { + ERR_FAIL_V(ERR_CANT_OPEN); + } + + ResourceLoaderText loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + err = loader.rename_dependencies(f, p_path, p_map); } - ResourceLoaderText loader; - loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); - loader.res_path = loader.local_path; - return loader.rename_dependencies(f, p_path, p_map); + if (err == OK) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + da->remove(p_path); + da->rename(p_path + ".depren", p_path); + } + + return err; } ResourceFormatLoaderText *ResourceFormatLoaderText::singleton = nullptr; Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) { Error err; - FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_src_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'."); @@ -1488,12 +1557,12 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, /*****************************************************************************************************/ /*****************************************************************************************************/ -String ResourceFormatSaverTextInstance::_write_resources(void *ud, const RES &p_resource) { - ResourceFormatSaverTextInstance *rsi = (ResourceFormatSaverTextInstance *)ud; +String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Resource> &p_resource) { + ResourceFormatSaverTextInstance *rsi = static_cast<ResourceFormatSaverTextInstance *>(ud); return rsi->_write_resource(p_resource); } -String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { +String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) { if (external_resources.has(res)) { return "ExtResource( \"" + external_resources[res] + "\" )"; } else { @@ -1516,7 +1585,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, bool p_main) { switch (p_variant.get_type()) { case Variant::OBJECT: { - RES res = p_variant; + Ref<Resource> res = p_variant; if (res.is_null() || external_resources.has(res)) { return; @@ -1553,7 +1622,7 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, Variant v = res->get(I->get().name); if (pi.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { - RES sres = v; + Ref<Resource> sres = v; if (sres.is_valid()) { NonPersistentKey npk; npk.base = res; @@ -1597,15 +1666,24 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant, } } -Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +static String _resource_get_class(Ref<Resource> p_resource) { + Ref<MissingResource> missing_resource = p_resource; + if (missing_resource.is_valid()) { + return missing_resource->get_original_class(); + } else { + return p_resource->get_class(); + } +} + +Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { if (p_path.ends_with(".tscn")) { packed_scene = p_resource; } Error err; - f = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, ERR_CANT_OPEN, "Cannot save file '" + p_path + "'."); - FileAccessRef _fref(f); + Ref<FileAccess> _fref(f); local_path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1638,7 +1716,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r { String title = packed_scene.is_valid() ? "[gd_scene " : "[gd_resource "; if (packed_scene.is_null()) { - title += "type=\"" + p_resource->get_class() + "\" "; + title += "type=\"" + _resource_get_class(p_resource) + "\" "; } int load_steps = saved_resources.size() + external_resources.size(); @@ -1659,8 +1737,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #ifdef TOOLS_ENABLED // Keep order from cached ids. - Set<String> cached_ids_found; - for (KeyValue<RES, String> &E : external_resources) { + 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)) { int sep_pos = E.value.find("_"); @@ -1676,7 +1754,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } } // Create IDs for non cached resources. - for (KeyValue<RES, String> &E : external_resources) { + for (KeyValue<Ref<Resource>, String> &E : external_resources) { if (cached_ids_found.has(E.value)) { // Already cached, go on. continue; } @@ -1698,14 +1776,14 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #else // Make sure to start from one, as it makes format more readable. int counter = 1; - for (KeyValue<RES, String> &E : external_resources) { + for (KeyValue<Ref<Resource>, String> &E : external_resources) { E.value = itos(counter++); } #endif Vector<ResourceSort> sorted_er; - for (const KeyValue<RES, String> &E : external_resources) { + for (const KeyValue<Ref<Resource>, String> &E : external_resources) { ResourceSort rs; rs.resource = E.key; rs.id = E.value; @@ -1731,10 +1809,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_line(String()); // Separate. } - Set<String> used_unique_ids; + RBSet<String> used_unique_ids; - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES res = E->get(); + for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) { + Ref<Resource> res = E->get(); if (E->next() && res->is_built_in()) { if (!res->get_scene_unique_id().is_empty()) { if (used_unique_ids.has(res->get_scene_unique_id())) { @@ -1746,8 +1824,8 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } } - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES res = E->get(); + for (List<Ref<Resource>>::Element *E = saved_resources.front(); E; E = E->next()) { + Ref<Resource> res = E->get(); ERR_CONTINUE(!resource_set.has(res)); bool main = (E->next() == nullptr); @@ -1762,7 +1840,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r if (res->get_scene_unique_id().is_empty()) { String new_id; while (true) { - new_id = res->get_class() + "_" + Resource::generate_scene_unique_id(); + new_id = _resource_get_class(res) + "_" + Resource::generate_scene_unique_id(); if (!used_unique_ids.has(new_id)) { break; @@ -1774,7 +1852,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } String id = res->get_scene_unique_id(); - line += "type=\"" + res->get_class() + "\" id=\"" + id; + line += "type=\"" + _resource_get_class(res) + "\" id=\"" + id; f->store_line(line + "\"]"); if (takeover_paths) { res->set_path(p_path + "::" + id, true); @@ -1786,12 +1864,17 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r #endif } + Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary()); + List<PropertyInfo> property_list; res->get_property_list(&property_list); for (List<PropertyInfo>::Element *PE = property_list.front(); PE; PE = PE->next()) { if (skip_editor && PE->get().name.begins_with("__editor")) { continue; } + if (PE->get().name == META_PROPERTY_MISSING_RESOURCES) { + continue; + } if (PE->get().usage & PROPERTY_USAGE_STORAGE) { String name = PE->get().name; @@ -1806,6 +1889,15 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } else { value = res->get(name); } + + if (PE->get().type == Variant::OBJECT && missing_resource_properties.has(PE->get().name)) { + // Was this missing resource overriden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[PE->get().name]; + } + } + Variant default_value = ClassDB::class_get_default_property_value(res->get_class(), name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { @@ -1942,16 +2034,13 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { - f->close(); return ERR_CANT_CREATE; } - f->close(); - return OK; } -Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverText::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) { return ERR_FILE_UNRECOGNIZED; } @@ -1960,11 +2049,11 @@ Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, return saver.save(p_path, p_resource, p_flags); } -bool ResourceFormatSaverText::recognize(const RES &p_resource) const { +bool ResourceFormatSaverText::recognize(const Ref<Resource> &p_resource) const { return true; // All resources recognized! } -void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverText::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (Ref<PackedScene>(p_resource).is_valid()) { p_extensions->push_back("tscn"); // Text scene. } else { diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 9585b9040f..adab503599 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -43,12 +43,12 @@ class ResourceLoaderText { String res_path; String error_text; - FileAccess *f = nullptr; + Ref<FileAccess> f; VariantParser::StreamFile stream; struct ExtResource { - RES cache; + Ref<Resource> cache; String path; String type; }; @@ -58,8 +58,8 @@ class ResourceLoaderText { bool ignore_resource_parsing = false; - Map<String, ExtResource> ext_resources; - Map<String, RES> 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,14 +90,14 @@ class ResourceLoaderText { }; struct DummyReadData { - Map<RES, int> external_resources; - Map<String, RES> rev_external_resources; - Map<RES, int> resource_index_map; - Map<String, RES> 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((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } - static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); } + 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); } + static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy(static_cast<DummyReadData *>(p_self), p_stream, r_res, line, r_err_str); } static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str); @@ -108,7 +108,7 @@ class ResourceLoaderText { Error error = OK; - RES resource; + Ref<Resource> resource; Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser); @@ -120,28 +120,27 @@ public: int get_stage_count() const; void set_translation_remapped(bool p_remapped); - void open(FileAccess *p_f, bool p_skip_first_tag = false); - String recognize(FileAccess *p_f); - ResourceUID::ID get_uid(FileAccess *p_f); - void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); - Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map); + void open(Ref<FileAccess> p_f, bool p_skip_first_tag = false); + 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 HashMap<String, String> &p_map); - Error save_as_binary(FileAccess *p_f, const String &p_path); + Error save_as_binary(Ref<FileAccess> p_f, const String &p_path); ResourceLoaderText(); - ~ResourceLoaderText(); }; class ResourceFormatLoaderText : public ResourceFormatLoader { public: static ResourceFormatLoaderText *singleton; - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; 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); @@ -157,23 +156,22 @@ class ResourceFormatSaverTextInstance { bool relative_paths = false; bool bundle_resources = false; bool skip_editor = false; - FileAccess *f = nullptr; struct NonPersistentKey { //for resource properties generated on the fly - RES base; + Ref<Resource> base; StringName property; bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; } }; - Map<NonPersistentKey, RES> non_persistent_map; + RBMap<NonPersistentKey, Ref<Resource>> non_persistent_map; - Set<RES> resource_set; - List<RES> saved_resources; - Map<RES, String> external_resources; - Map<RES, String> internal_resources; + RBSet<Ref<Resource>> resource_set; + List<Ref<Resource>> saved_resources; + HashMap<Ref<Resource>, String> external_resources; + HashMap<Ref<Resource>, String> internal_resources; struct ResourceSort { - RES resource; + Ref<Resource> resource; String id; bool operator<(const ResourceSort &p_right) const { return id.naturalnocasecmp_to(p_right.id) < 0; @@ -182,19 +180,19 @@ class ResourceFormatSaverTextInstance { void _find_resources(const Variant &p_variant, bool p_main = false); - static String _write_resources(void *ud, const RES &p_resource); - String _write_resource(const RES &res); + static String _write_resources(void *ud, const Ref<Resource> &p_resource); + String _write_resource(const Ref<Resource> &res); public: - Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); }; class ResourceFormatSaverText : public ResourceFormatSaver { public: static ResourceFormatSaverText *singleton; - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual bool recognize(const RES &p_resource) const; - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual bool recognize(const Ref<Resource> &p_resource) const; + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; ResourceFormatSaverText(); }; diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index ce7fcb199d..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); } } @@ -172,7 +172,7 @@ Shader::~Shader() { //////////// -RES ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; } @@ -210,29 +210,26 @@ String ResourceFormatLoaderShader::get_resource_type(const String &p_path) const return ""; } -Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { +Error ResourceFormatSaverShader::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) { Ref<Shader> shader = p_resource; ERR_FAIL_COND_V(shader.is_null(), ERR_INVALID_PARAMETER); String source = shader->get_code(); Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); ERR_FAIL_COND_V_MSG(err, err, "Cannot save shader '" + p_path + "'."); file->store_string(source); if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { - memdelete(file); return ERR_CANT_CREATE; } - file->close(); - memdelete(file); return OK; } -void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { +void ResourceFormatSaverShader::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { if (const Shader *shader = Object::cast_to<Shader>(*p_resource)) { if (shader->is_text_shader()) { p_extensions->push_back("gdshader"); @@ -240,6 +237,6 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, } } -bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { +bool ResourceFormatSaverShader::recognize(const Ref<Resource> &p_resource) const { return p_resource->get_class_name() == "Shader"; //only shader, not inherited } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index d05ec06819..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(); } @@ -103,7 +103,7 @@ VARIANT_ENUM_CAST(Shader::Mode); class ResourceFormatLoaderShader : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -111,9 +111,9 @@ public: class ResourceFormatSaverShader : public ResourceFormatSaver { public: - virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const RES &p_resource) const; + virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; }; #endif // SHADER_H diff --git a/scene/resources/skeleton_modification_2d.h b/scene/resources/skeleton_modification_2d.h index d49f9e7f51..3b9235ffd8 100644 --- a/scene/resources/skeleton_modification_2d.h +++ b/scene/resources/skeleton_modification_2d.h @@ -49,7 +49,7 @@ class SkeletonModification2D : public Resource { protected: static void _bind_methods(); - SkeletonModificationStack2D *stack; + SkeletonModificationStack2D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; diff --git a/scene/resources/skeleton_modification_2d_physicalbones.h b/scene/resources/skeleton_modification_2d_physicalbones.h index d53102fa5e..373ff666ee 100644 --- a/scene/resources/skeleton_modification_2d_physicalbones.h +++ b/scene/resources/skeleton_modification_2d_physicalbones.h @@ -52,7 +52,7 @@ private: bool _simulation_state_dirty = false; TypedArray<StringName> _simulation_state_dirty_names; - bool _simulation_state_dirty_process; + bool _simulation_state_dirty_process = false; void _update_simulation_state(); protected: diff --git a/scene/resources/skeleton_modification_3d.h b/scene/resources/skeleton_modification_3d.h index a81c0c38bd..ab736fcbd2 100644 --- a/scene/resources/skeleton_modification_3d.h +++ b/scene/resources/skeleton_modification_3d.h @@ -44,7 +44,7 @@ class SkeletonModification3D : public Resource { protected: static void _bind_methods(); - SkeletonModificationStack3D *stack; + SkeletonModificationStack3D *stack = nullptr; int execution_mode = 0; // 0 = process bool enabled = true; diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index b944c244b6..38ec19828f 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -263,7 +263,7 @@ void SkeletonModificationStack2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); } SkeletonModificationStack2D::SkeletonModificationStack2D() { diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp index 7ccba1228c..44fbfc934e 100644 --- a/scene/resources/skeleton_modification_stack_3d.cpp +++ b/scene/resources/skeleton_modification_stack_3d.cpp @@ -217,7 +217,7 @@ void SkeletonModificationStack3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1"), "set_modification_count", "get_modification_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count"); } SkeletonModificationStack3D::SkeletonModificationStack3D() { 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/sky_material.cpp b/scene/resources/sky_material.cpp index 54543782f6..7874d77298 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -144,6 +144,15 @@ float ProceduralSkyMaterial::get_sun_curve() const { return sun_curve; } +void ProceduralSkyMaterial::set_dither_strength(float p_dither_strength) { + dither_strength = p_dither_strength; + RS::get_singleton()->material_set_param(_get_material(), "dither_strength", dither_strength); +} + +float ProceduralSkyMaterial::get_dither_strength() const { + return dither_strength; +} + Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } @@ -199,6 +208,9 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sun_curve", "curve"), &ProceduralSkyMaterial::set_sun_curve); ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve); + ClassDB::bind_method(D_METHOD("set_dither_strength", "strength"), &ProceduralSkyMaterial::set_dither_strength); + ClassDB::bind_method(D_METHOD("get_dither_strength"), &ProceduralSkyMaterial::get_dither_strength); + ADD_GROUP("Sky", "sky_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); @@ -216,6 +228,9 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_GROUP("Sun", "sun_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_angle_max", "get_sun_angle_max"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve"); + + ADD_GROUP("", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength"); } void ProceduralSkyMaterial::cleanup_shader() { @@ -247,6 +262,14 @@ uniform float ground_curve : hint_range(0, 1) = 0.02; uniform float ground_energy = 1.0; uniform float sun_angle_max = 30.0; uniform float sun_curve : hint_range(0, 1) = 0.15; +uniform float dither_strength : hint_range(0, 10) = 1.0; + +// From: https://www.shadertoy.com/view/4sfGzS credit to iq +float hash(vec3 p) { + p = fract( p * 0.3183099 + 0.1 ); + p *= 17.0; + return fract(p.x * p.y * p.z * (p.x + p.y + p.z)); +} void sky() { float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0)); @@ -302,6 +325,9 @@ void sky() { ground *= ground_energy; COLOR = mix(ground, sky, step(0.0, EYEDIR.y)); + + // Make optional, eliminates banding. + COLOR += (hash(EYEDIR * 1741.9782) * 0.08 - 0.04) * 0.016 * dither_strength; } )"); } @@ -322,6 +348,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sun_angle_max(30.0); set_sun_curve(0.15); + set_dither_strength(1.0); } ProceduralSkyMaterial::~ProceduralSkyMaterial() { @@ -404,8 +431,11 @@ void PanoramaSkyMaterial::_update_shader() { // Add a comment to describe the shader origin (useful when converting to ShaderMaterial). RS::get_singleton()->shader_set_code(shader_cache[i], vformat(R"( // NOTE: Shader automatically converted from )" VERSION_NAME " " VERSION_FULL_CONFIG R"('s PanoramaSkyMaterial. + shader_type sky; -uniform sampler2D source_panorama : %s, hint_albedo; + +uniform sampler2D source_panorama : %s, hint_black_albedo; + void sky() { COLOR = texture(source_panorama, SKY_COORDS).rgb; } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 5c791a185a..8163a42519 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -40,18 +40,19 @@ class ProceduralSkyMaterial : public Material { private: Color sky_top_color; Color sky_horizon_color; - float sky_curve; - float sky_energy; + float sky_curve = 0.0f; + float sky_energy = 0.0f; Ref<Texture2D> sky_cover; Color sky_cover_modulate; Color ground_bottom_color; Color ground_horizon_color; - float ground_curve; - float ground_energy; + float ground_curve = 0.0f; + float ground_energy = 0.0f; - float sun_angle_max; - float sun_curve; + float sun_angle_max = 0.0f; + float sun_curve = 0.0f; + float dither_strength = 0.0f; static Mutex shader_mutex; static RID shader; @@ -98,6 +99,9 @@ public: void set_sun_curve(float p_curve); float get_sun_curve() const; + void set_dither_strength(float p_dither_strength); + float get_dither_strength() const; + virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; virtual RID get_rid() const override; @@ -154,16 +158,16 @@ private: static Mutex shader_mutex; static RID shader; - float rayleigh; + float rayleigh = 0.0f; Color rayleigh_color; - float mie; - float mie_eccentricity; + float mie = 0.0f; + float mie_eccentricity = 0.0f; Color mie_color; - float turbidity; - float sun_disk_scale; + float turbidity = 0.0f; + float sun_disk_scale = 0.0f; Color ground_color; - float exposure; - float dither_strength; + float exposure = 0.0f; + float dither_strength = 0.0f; Ref<Texture2D> night_sky; static void _update_shader(); mutable bool shader_set = false; diff --git a/scene/resources/sphere_shape_3d.h b/scene/resources/sphere_shape_3d.h index ff6d883940..20887dd092 100644 --- a/scene/resources/sphere_shape_3d.h +++ b/scene/resources/sphere_shape_3d.h @@ -35,7 +35,7 @@ class SphereShape3D : public Shape3D { GDCLASS(SphereShape3D, Shape3D); - float radius; + float radius = 1.0f; protected: static void _bind_methods(); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index ece126791e..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]; } } @@ -195,7 +195,7 @@ void SpriteFrames::_set_animations(const Array &p_animations) { anim.loop = d["loop"]; Array frames = d["frames"]; for (int j = 0; j < frames.size(); j++) { - RES res = frames[j]; + Ref<Resource> res = frames[j]; anim.frames.push_back(res); } 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/style_box.cpp b/scene/resources/style_box.cpp index fe52761482..f3cb2b9ea7 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -124,8 +124,8 @@ void StyleBox::_bind_methods() { ADD_GROUP("Content Margin", "content_margin_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_left", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_top", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_right", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "content_margin_bottom", PROPERTY_HINT_RANGE, "-1,2048,1"), "set_default_margin", "get_default_margin", SIDE_BOTTOM); GDVIRTUAL_BIND(_get_style_margin, "side") @@ -316,19 +316,23 @@ void StyleBoxTexture::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); + ADD_GROUP("Margin", "margin_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_margin_size", "get_margin_size", SIDE_BOTTOM); + ADD_GROUP("Expand Margin", "expand_margin_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin_size", "get_expand_margin_size", SIDE_BOTTOM); + ADD_GROUP("Axis Stretch", "axis_stretch_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode"); + ADD_GROUP("Modulate", "modulate_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate_color"), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); @@ -457,6 +461,15 @@ bool StyleBoxFlat::is_draw_center_enabled() const { return draw_center; } +void StyleBoxFlat::set_skew(Vector2 p_skew) { + skew = p_skew; + emit_changed(); +} + +Vector2 StyleBoxFlat::get_skew() const { + return skew; +} + void StyleBoxFlat::set_shadow_color(const Color &p_color) { shadow_color = p_color; emit_changed(); @@ -542,7 +555,7 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re } inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4], - const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const bool fill_center = false) { + const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool fill_center = false) { int vert_offset = verts.size(); if (!vert_offset) { vert_offset = 0; @@ -586,9 +599,12 @@ inline void draw_ring(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color color = outer_color; corner_point = outer_points[corner_index]; } - real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; - real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; - verts.push_back(Vector2(x, y)); + + const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; + const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; + const float x_skew = -skew.x * (y - ring_rect.get_center().y); + const float y_skew = -skew.y * (x - ring_rect.get_center().x); + verts.push_back(Vector2(x + x_skew, y + y_skew)); colors.push_back(color); } } @@ -666,10 +682,12 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { return; } - bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); - bool aa_on = rounded_corners && anti_aliased; + const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); + // Only enable antialiasing if it is actually needed. This improve performances + // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. + const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased; - bool blend_on = blend_border && draw_border; + const bool blend_on = blend_border && draw_border; Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); Color border_color_blend = (draw_center ? bg_color : border_color_alpha); @@ -716,24 +734,24 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail); + shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew); if (draw_center) { draw_ring(verts, indices, colors, shadow_inner_rect, adapted_corner, - shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, true); + shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true); } } // Create border (no AA). if (draw_border && !aa_on) { draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - border_style_rect, infill_rect, border_color_inner, border_color, corner_detail); + border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew); } // Create infill (no AA). if (draw_center && (!aa_on || blend_on || !draw_border)) { draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect, infill_rect, bg_color, bg_color, corner_detail, true); + infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true); } if (aa_on) { @@ -765,7 +783,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { aa_border_width[SIDE_RIGHT], aa_border_width[SIDE_BOTTOM]); // Create infill within AA border. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, true); + infill_inner_rect_aa, infill_inner_rect_aa, bg_color, bg_color, corner_detail, skew, true); } if (!blend_on || !draw_border) { @@ -776,7 +794,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { // Create infill fake AA gradient. draw_ring(verts, indices, colors, style_rect, adapted_corner, - infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail); + infill_rect_aa, infill_rect, bg_color, alpha_bg, corner_detail, skew); } } @@ -790,17 +808,17 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { // Create border. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail); + border_style_rect_aa, ((blend_on) ? infill_rect : infill_rect_aa), border_color_inner, border_color, corner_detail, skew); if (!blend_on) { // Create inner border fake AA gradient. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail); + infill_rect_aa, infill_rect, border_color_blend, border_color, corner_detail, skew); } // Create outer border fake AA gradient. draw_ring(verts, indices, colors, border_style_rect, adapted_corner, - style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail); + style_rect_aa, border_style_rect_aa, border_color, border_color_alpha, corner_detail, skew); } } @@ -858,6 +876,9 @@ void StyleBoxFlat::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &StyleBoxFlat::set_draw_center); ClassDB::bind_method(D_METHOD("is_draw_center_enabled"), &StyleBoxFlat::is_draw_center_enabled); + ClassDB::bind_method(D_METHOD("set_skew", "skew"), &StyleBoxFlat::set_skew); + ClassDB::bind_method(D_METHOD("get_skew"), &StyleBoxFlat::get_skew); + ClassDB::bind_method(D_METHOD("set_shadow_color", "color"), &StyleBoxFlat::set_shadow_color); ClassDB::bind_method(D_METHOD("get_shadow_color"), &StyleBoxFlat::get_shadow_color); @@ -879,6 +900,7 @@ void StyleBoxFlat::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color"), "set_bg_color", "get_bg_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center"), "set_draw_center", "is_draw_center_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew"), "set_skew", "get_skew"); ADD_GROUP("Border Width", "border_width_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left", PROPERTY_HINT_RANGE, "0,1024,1"), "set_border_width", "get_border_width", SIDE_LEFT); @@ -901,8 +923,8 @@ void StyleBoxFlat::_bind_methods() { ADD_GROUP("Expand Margin", "expand_margin_"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_LEFT); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom", PROPERTY_HINT_RANGE, "0,2048,1"), "set_expand_margin", "get_expand_margin", SIDE_BOTTOM); ADD_GROUP("Shadow", "shadow_"); diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index 68ad41b69c..3b3654775f 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -155,6 +155,7 @@ class StyleBoxFlat : public StyleBox { bool draw_center = true; bool blend_border = false; + Vector2 skew; bool anti_aliased = true; int corner_detail = 8; @@ -200,6 +201,9 @@ public: void set_draw_center(bool p_enabled); bool is_draw_center_enabled() const; + void set_skew(Vector2 p_skew); + Vector2 get_skew() const; + void set_shadow_color(const Color &p_color); Color get_shadow_color() const; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 8ff1fde2cf..0e41eed570 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1165,7 +1165,7 @@ void SurfaceTool::generate_normals(bool p_flip) { for (int i = 0; i < 3; i++) { Vector3 *lv = vertex_hash.getptr(v[i]); if (!lv) { - vertex_hash.set(v[i], normal); + vertex_hash.insert(v[i], normal); } else { (*lv) += normal; } 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_file.cpp b/scene/resources/text_file.cpp index cbfee754e2..96a47c37c4 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -51,7 +51,7 @@ void TextFile::reload_from_file() { Error TextFile::load_text(const String &p_path) { Vector<uint8_t> sourcef; Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'."); @@ -59,8 +59,7 @@ Error TextFile::load_text(const String &p_path) { sourcef.resize(len + 1); uint8_t *w = sourcef.ptrw(); uint64_t r = f->get_buffer(w, len); - f->close(); - memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); w[len] = 0; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index db5f1338db..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); @@ -327,10 +327,18 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((width - length) / 2.0); - } else { - ofs.y += Math::floor((width - length) / 2.0); + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -366,10 +374,18 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((width - length) / 2.0); - } else { - ofs.y += Math::floor((width - length) / 2.0); + if (length <= width) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((width - length) / 2.0); + } else { + ofs.y += Math::floor((width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += width - length; + } else { + ofs.y += width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index d74d7c88c6..61adaf43dd 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -609,10 +609,18 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((l_width - line_width) / 2.0); - } else { - ofs.y += Math::floor((l_width - line_width) / 2.0); + if (line_width <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - line_width) / 2.0); + } else { + ofs.y += Math::floor((l_width - line_width) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - line_width; + } else { + ofs.y += l_width - line_width; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { @@ -701,10 +709,18 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli case HORIZONTAL_ALIGNMENT_LEFT: break; case HORIZONTAL_ALIGNMENT_CENTER: { - if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.x += Math::floor((l_width - length) / 2.0); - } else { - ofs.y += Math::floor((l_width - length) / 2.0); + if (length <= l_width) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += Math::floor((l_width - length) / 2.0); + } else { + ofs.y += Math::floor((l_width - length) / 2.0); + } + } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) { + if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { + ofs.x += l_width - length; + } else { + ofs.y += l_width - length; + } } } break; case HORIZONTAL_ALIGNMENT_RIGHT: { diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a66d9d9f27..5850d253e3 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -646,7 +646,7 @@ PortableCompressedTexture2D::~PortableCompressedTexture2D() { ////////////////////////////////////////// -Ref<Image> CompressedTexture2D::load_image_from_file(FileAccess *f, int p_size_limit) { +Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_size_limit) { uint32_t data_format = f->get_32(); uint32_t w = f->get_16(); uint32_t h = f->get_16(); @@ -821,20 +821,18 @@ Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER); - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); if (header[0] != 'G' || header[1] != 'S' || header[2] != 'T' || header[3] != '2') { - memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is corrupt (Bad header)."); } uint32_t version = f->get_32(); if (version > FORMAT_VERSION) { - memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Compressed texture file is too new."); } r_width = f->get_32(); @@ -867,8 +865,6 @@ Error CompressedTexture2D::_load_data(const String &p_path, int &r_width, int &r image = load_image_from_file(f, p_size_limit); - memdelete(f); - if (image.is_null() || image->is_empty()) { return ERR_CANT_OPEN; } @@ -1062,7 +1058,7 @@ CompressedTexture2D::~CompressedTexture2D() { } } -RES ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<CompressedTexture2D> st; st.instantiate(); Error err = st->load(p_path); @@ -1070,7 +1066,7 @@ RES ResourceFormatLoaderCompressedTexture2D::load(const String &p_path, const St *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } return st; @@ -1142,7 +1138,7 @@ bool Texture3D::has_mipmaps() const { if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) { return ret; } - return 0; + return false; } Vector<Ref<Image>> Texture3D::get_data() const { @@ -1272,8 +1268,8 @@ Image::Format CompressedTexture3D::get_format() const { } Error CompressedTexture3D::_load_data(const String &p_path, Vector<Ref<Image>> &r_data, Image::Format &r_format, int &r_width, int &r_height, int &r_depth, bool &r_mipmaps) { - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); @@ -1420,7 +1416,7 @@ CompressedTexture3D::~CompressedTexture3D() { ///////////////////////////// -RES ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<CompressedTexture3D> st; st.instantiate(); Error err = st->load(p_path); @@ -1428,7 +1424,7 @@ RES ResourceFormatLoaderCompressedTexture3D::load(const String &p_path, const St *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } return st; @@ -1721,8 +1717,8 @@ void MeshTexture::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_mo Transform2D xform; xform.set_origin(p_pos); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -1743,8 +1739,8 @@ void MeshTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, xform.set_scale(p_rect.size / size); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -1765,8 +1761,8 @@ void MeshTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const xform.set_scale(p_rect.size / size); if (p_transpose) { - SWAP(xform.elements[0][1], xform.elements[1][0]); - SWAP(xform.elements[0][0], xform.elements[1][1]); + SWAP(xform.columns[0][1], xform.columns[1][0]); + SWAP(xform.columns[0][0], xform.columns[1][1]); } RenderingServer::get_singleton()->canvas_item_add_mesh(p_canvas_item, mesh->get_rid(), xform, p_modulate, base_texture->get_rid()); } @@ -2282,6 +2278,8 @@ Ref<Image> GradientTexture1D::get_image() const { return RenderingServer::get_singleton()->texture_2d_get(texture); } +////////////////// + GradientTexture2D::GradientTexture2D() { _queue_update(); } @@ -2303,7 +2301,8 @@ void GradientTexture2D::set_gradient(Ref<Gradient> p_gradient) { if (gradient.is_valid()) { gradient->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &GradientTexture2D::_queue_update)); } - _queue_update(); + _update(); + emit_changed(); } Ref<Gradient> GradientTexture2D::get_gradient() const { @@ -3092,8 +3091,8 @@ Image::Format CompressedTextureLayered::get_format() const { Error CompressedTextureLayered::_load_data(const String &p_path, Vector<Ref<Image>> &images, int &mipmap_limit, int p_size_limit) { ERR_FAIL_COND_V(images.size() != 0, ERR_INVALID_PARAMETER); - FileAccessRef f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Unable to open file: %s.", p_path)); uint8_t header[4]; f->get_buffer(header, 4); @@ -3244,7 +3243,7 @@ CompressedTextureLayered::~CompressedTextureLayered() { ///////////////////////////////////////////////// -RES ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { +Ref<Resource> ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<CompressedTextureLayered> ct; if (p_path.get_extension().to_lower() == "ctexarray") { Ref<CompressedTexture2DArray> c; @@ -3262,14 +3261,14 @@ RES ResourceFormatLoaderCompressedTextureLayered::load(const String &p_path, con if (r_error) { *r_error = ERR_FILE_UNRECOGNIZED; } - return RES(); + return Ref<Resource>(); } Error err = ct->load(p_path); if (r_error) { *r_error = err; } if (err != OK) { - return RES(); + return Ref<Resource>(); } return ct; @@ -3396,3 +3395,148 @@ CameraTexture::~CameraTexture() { RenderingServer::get_singleton()->free(_texture); } } + +/////////////////////////// + +void PlaceholderTexture2D::set_size(Size2 p_size) { + size = p_size; +} + +int PlaceholderTexture2D::get_width() const { + return size.width; +} + +int PlaceholderTexture2D::get_height() const { + return size.height; +} + +bool PlaceholderTexture2D::has_alpha() const { + return false; +} + +Ref<Image> PlaceholderTexture2D::get_image() const { + return Ref<Image>(); +} + +RID PlaceholderTexture2D::get_rid() const { + return rid; +} + +void PlaceholderTexture2D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture2D::set_size); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); +} + +PlaceholderTexture2D::PlaceholderTexture2D() { + rid = RS::get_singleton()->texture_2d_placeholder_create(); +} + +PlaceholderTexture2D::~PlaceholderTexture2D() { + RS::get_singleton()->free(rid); +} + +/////////////////////////////////////////////// + +void PlaceholderTexture3D::set_size(const Vector3i &p_size) { + size = p_size; +} + +Vector3i PlaceholderTexture3D::get_size() const { + return size; +} + +Image::Format PlaceholderTexture3D::get_format() const { + return Image::FORMAT_RGB8; +} + +int PlaceholderTexture3D::get_width() const { + return size.x; +} + +int PlaceholderTexture3D::get_height() const { + return size.y; +} + +int PlaceholderTexture3D::get_depth() const { + return size.z; +} + +bool PlaceholderTexture3D::has_mipmaps() const { + return false; +} + +Vector<Ref<Image>> PlaceholderTexture3D::get_data() const { + return Vector<Ref<Image>>(); +} + +void PlaceholderTexture3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTexture3D::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTexture3D::get_size); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3I, "size"), "set_size", "get_size"); +} + +PlaceholderTexture3D::PlaceholderTexture3D() { + rid = RS::get_singleton()->texture_3d_placeholder_create(); +} +PlaceholderTexture3D::~PlaceholderTexture3D() { + RS::get_singleton()->free(rid); +} + +///////////////////////////////////////////////// + +void PlaceholderTextureLayered::set_size(const Size2i &p_size) { + size = p_size; +} + +Size2i PlaceholderTextureLayered::get_size() const { + return size; +} + +void PlaceholderTextureLayered::set_layers(int p_layers) { + layers = p_layers; +} + +Image::Format PlaceholderTextureLayered::get_format() const { + return Image::FORMAT_RGB8; +} + +TextureLayered::LayeredType PlaceholderTextureLayered::get_layered_type() const { + return layered_type; +} + +int PlaceholderTextureLayered::get_width() const { + return size.x; +} + +int PlaceholderTextureLayered::get_height() const { + return size.y; +} + +int PlaceholderTextureLayered::get_layers() const { + return layers; +} + +bool PlaceholderTextureLayered::has_mipmaps() const { + return false; +} + +Ref<Image> PlaceholderTextureLayered::get_layer_data(int p_layer) const { + return Ref<Image>(); +} + +void PlaceholderTextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_size", "size"), &PlaceholderTextureLayered::set_size); + ClassDB::bind_method(D_METHOD("get_size"), &PlaceholderTextureLayered::get_size); + ClassDB::bind_method(D_METHOD("set_layers", "layers"), &PlaceholderTextureLayered::set_layers); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_RANGE, "1,4096"), "set_layers", "get_layers"); +} + +PlaceholderTextureLayered::PlaceholderTextureLayered(LayeredType p_type) { + layered_type = p_type; + rid = RS::get_singleton()->texture_2d_layered_placeholder_create(RS::TextureLayeredType(layered_type)); +} +PlaceholderTextureLayered::~PlaceholderTextureLayered() { + RS::get_singleton()->free(rid); +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 7e194fd21d..317756e313 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -253,7 +253,7 @@ protected: void _validate_property(PropertyInfo &property) const override; public: - static Ref<Image> load_image_from_file(FileAccess *p_file, int p_size_limit); + static Ref<Image> load_image_from_file(Ref<FileAccess> p_file, int p_size_limit); typedef void (*TextureFormatRequestCallback)(const Ref<CompressedTexture2D> &); typedef void (*TextureFormatRoughnessRequestCallback)(const Ref<CompressedTexture2D> &, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_roughness_channel); @@ -287,7 +287,7 @@ public: class ResourceFormatLoaderCompressedTexture2D : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -552,7 +552,7 @@ public: class ResourceFormatLoaderCompressedTextureLayered : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -673,7 +673,7 @@ public: class ResourceFormatLoaderCompressedTexture3D : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; virtual String get_resource_type(const String &p_path) const; @@ -1011,4 +1011,98 @@ public: ~CameraTexture(); }; +class PlaceholderTexture2D : public Texture2D { + GDCLASS(PlaceholderTexture2D, Texture2D) + + RID rid; + Size2 size = Size2(1, 1); + +protected: + static void _bind_methods(); + +public: + void set_size(Size2 p_size); + + virtual int get_width() const override; + virtual int get_height() const override; + virtual RID get_rid() const override; + virtual bool has_alpha() const override; + + virtual Ref<Image> get_image() const override; + + PlaceholderTexture2D(); + ~PlaceholderTexture2D(); +}; + +class PlaceholderTexture3D : public Texture3D { + GDCLASS(PlaceholderTexture3D, Texture3D) + + RID rid; + Vector3i size = Vector3i(1, 1, 1); + +protected: + static void _bind_methods(); + +public: + void set_size(const Vector3i &p_size); + Vector3i get_size() const; + virtual Image::Format get_format() const override; + virtual int get_width() const override; + virtual int get_height() const override; + virtual int get_depth() const override; + virtual bool has_mipmaps() const override; + virtual Vector<Ref<Image>> get_data() const override; + + PlaceholderTexture3D(); + ~PlaceholderTexture3D(); +}; + +class PlaceholderTextureLayered : public TextureLayered { + GDCLASS(PlaceholderTextureLayered, TextureLayered) + + RID rid; + Size2i size = Size2i(1, 1); + int layers = 1; + LayeredType layered_type = LAYERED_TYPE_2D_ARRAY; + +protected: + static void _bind_methods(); + +public: + void set_size(const Size2i &p_size); + Size2i get_size() const; + void set_layers(int p_layers); + virtual Image::Format get_format() const override; + virtual LayeredType get_layered_type() const override; + virtual int get_width() const override; + virtual int get_height() const override; + virtual int get_layers() const override; + virtual bool has_mipmaps() const override; + virtual Ref<Image> get_layer_data(int p_layer) const override; + + PlaceholderTextureLayered(LayeredType p_type); + ~PlaceholderTextureLayered(); +}; + +class PlaceholderTexture2DArray : public PlaceholderTextureLayered { + GDCLASS(PlaceholderTexture2DArray, PlaceholderTextureLayered) +public: + PlaceholderTexture2DArray() : + PlaceholderTextureLayered(LAYERED_TYPE_2D_ARRAY) {} +}; + +class PlaceholderCubemap : public PlaceholderTextureLayered { + GDCLASS(PlaceholderCubemap, PlaceholderTextureLayered) +public: + PlaceholderCubemap() : + PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP) {} +}; + +class PlaceholderCubemapArray : public PlaceholderTextureLayered { + GDCLASS(PlaceholderCubemapArray, PlaceholderTextureLayered) +public: + PlaceholderCubemapArray() : + PlaceholderTextureLayered(LAYERED_TYPE_CUBEMAP_ARRAY) {} +}; + #endif diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 373fbb94ea..1e57933000 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -123,82 +123,64 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const { void Theme::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> list; - const StringName *key = nullptr; - // Type variations. - while ((key = variation_map.next(key))) { - list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type")); + for (const KeyValue<StringName, StringName> &E : variation_map) { + list.push_back(PropertyInfo(Variant::STRING_NAME, String() + E.key + "/base_type")); } - key = nullptr; - // Icons. - while ((key = icon_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = icon_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &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)); } } - key = nullptr; - // Styles. - while ((key = style_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = style_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &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)); } } - key = nullptr; - // Fonts. - while ((key = font_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = font_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL)); + for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &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)); } } - key = nullptr; - // Font sizes. - while ((key = font_size_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = font_size_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + for (const KeyValue<StringName, HashMap<StringName, int>> &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")); } } - key = nullptr; - // Colors. - while ((key = color_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = color_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2)); + for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) { + for (const KeyValue<StringName, Color> &F : E.value) { + list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/colors/" + F.key)); } } - key = nullptr; - // Constants. - while ((key = constant_map.next(key))) { - const StringName *key2 = nullptr; - - while ((key2 = constant_map[*key].next(key2))) { - list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2)); + for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) { + for (const KeyValue<StringName, int> &F : E.value) { + list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key)); } } // 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); } } @@ -261,6 +243,27 @@ int Theme::get_fallback_font_size() { return fallback_font_size; } +bool Theme::is_valid_type_name(const String &p_name) { + for (int i = 0; i < p_name.length(); i++) { + if (!is_ascii_identifier_char(p_name[i])) { + return false; + } + } + return true; +} + +bool Theme::is_valid_item_name(const String &p_name) { + if (p_name.is_empty()) { + return false; + } + for (int i = 0; i < p_name.length(); i++) { + if (!is_ascii_identifier_char(p_name[i])) { + return false; + } + } + return true; +} + // Fallback values for theme item types, configurable per theme. void Theme::set_default_base_scale(float p_base_scale) { if (default_base_scale == p_base_scale) { @@ -326,6 +329,9 @@ bool Theme::has_default_font_size() const { // Icons. void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -358,6 +364,8 @@ bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme } void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist."); @@ -388,14 +396,14 @@ void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) con return; } - const StringName *key = nullptr; - - while ((key = icon_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_icon_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (icon_map.has(p_theme_type)) { return; } @@ -409,9 +417,8 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = icon_map[p_theme_type].next(L))) { - Ref<Texture2D> icon = icon_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) { + Ref<Texture2D> icon = E.value; if (icon.is_valid()) { icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -425,14 +432,16 @@ 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); - const StringName *key = nullptr; - while ((key = icon_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) { + p_list->push_back(E.key); } } // Styleboxes. void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -465,6 +474,8 @@ bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_t } void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist."); @@ -495,14 +506,14 @@ void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) return; } - const StringName *key = nullptr; - - while ((key = style_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_stylebox_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (style_map.has(p_theme_type)) { return; } @@ -516,9 +527,8 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = style_map[p_theme_type].next(L))) { - Ref<StyleBox> style = style_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) { + Ref<StyleBox> style = E.value; if (style.is_valid()) { style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -532,14 +542,16 @@ 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); - const StringName *key = nullptr; - while ((key = style_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) { + p_list->push_back(E.key); } } // Fonts. void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = false; if (font_map[p_theme_type][p_name].is_valid()) { existing = true; @@ -574,6 +586,8 @@ bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme } void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist."); @@ -604,14 +618,14 @@ void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) con return; } - const StringName *key = nullptr; - - while ((key = font_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_font_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (font_map.has(p_theme_type)) { return; } @@ -625,9 +639,8 @@ void Theme::remove_font_type(const StringName &p_theme_type) { _freeze_change_propagation(); - const StringName *L = nullptr; - while ((L = font_map[p_theme_type].next(L))) { - Ref<Font> font = font_map[p_theme_type][*L]; + for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) { + Ref<Font> font = E.value; if (font.is_valid()) { font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } @@ -641,14 +654,16 @@ 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); - const StringName *key = nullptr; - while ((key = font_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) { + p_list->push_back(E.key); } } // Font sizes. void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_font_size_nocheck(p_name, p_theme_type); font_size_map[p_theme_type][p_name] = p_font_size; @@ -674,6 +689,8 @@ bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_ } void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist."); @@ -700,14 +717,14 @@ void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list return; } - const StringName *key = nullptr; - - while ((key = font_size_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, int> &E : font_size_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_font_size_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (font_size_map.has(p_theme_type)) { return; } @@ -725,14 +742,16 @@ 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); - const StringName *key = nullptr; - while ((key = font_size_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, int>> &E : font_size_map) { + p_list->push_back(E.key); } } // Colors. void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_color_nocheck(p_name, p_theme_type); color_map[p_theme_type][p_name] = p_color; @@ -756,6 +775,8 @@ bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_them } void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist."); @@ -782,14 +803,14 @@ void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) co return; } - const StringName *key = nullptr; - - while ((key = color_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, Color> &E : color_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_color_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (color_map.has(p_theme_type)) { return; } @@ -807,14 +828,16 @@ 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); - const StringName *key = nullptr; - while ((key = color_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, Color>> &E : color_map) { + p_list->push_back(E.key); } } // Theme constants. void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + bool existing = has_constant_nocheck(p_name, p_theme_type); constant_map[p_theme_type][p_name] = p_constant; @@ -838,6 +861,8 @@ bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_t } void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist."); ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists."); ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist."); @@ -864,14 +889,14 @@ void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) return; } - const StringName *key = nullptr; - - while ((key = constant_map[p_theme_type].next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, int> &E : constant_map[p_theme_type]) { + p_list->push_back(E.key); } } void Theme::add_constant_type(const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + if (constant_map.has(p_theme_type)) { return; } @@ -889,9 +914,8 @@ 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); - const StringName *key = nullptr; - while ((key = constant_map.next(key))) { - p_list->push_back(*key); + for (const KeyValue<StringName, HashMap<StringName, int>> &E : constant_map) { + p_list->push_back(E.key); } } @@ -1154,6 +1178,8 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l // Theme type variations. void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + ERR_FAIL_COND_MSG(!is_valid_type_name(p_base_type), vformat("Invalid type name: '%s'", p_base_type)); ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type."); ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type."); ERR_FAIL_COND_MSG(p_base_type == StringName(), "An empty theme type cannot be the base type of a variation. Use clear_type_variation() instead if you want to unmark '" + String(p_theme_type) + "' as a variation."); @@ -1246,52 +1272,12 @@ void Theme::remove_type(const StringName &p_theme_type) { void Theme::get_type_list(List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); - Set<StringName> types; - const StringName *key = nullptr; - - // Icons. - while ((key = icon_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // StyleBoxes. - while ((key = style_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Fonts. - while ((key = font_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Font sizes. - while ((key = font_size_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Colors. - while ((key = color_map.next(key))) { - types.insert(*key); - } - - key = nullptr; - - // Constants. - while ((key = constant_map.next(key))) { - types.insert(*key); - } - - for (Set<StringName>::Element *E = types.front(); E; E = E->next()) { - p_list->push_back(E->get()); - } + 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); } void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) { @@ -1602,75 +1588,62 @@ void Theme::merge_with(const Ref<Theme> &p_other) { // Colors. { - const StringName *K = nullptr; - while ((K = p_other->color_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->color_map[*K].next(L))) { - set_color(*L, *K, p_other->color_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, Color>> &E : p_other->color_map) { + for (const KeyValue<StringName, Color> &F : E.value) { + set_color(F.key, E.key, F.value); } } } // Constants. { - const StringName *K = nullptr; - while ((K = p_other->constant_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->constant_map[*K].next(L))) { - set_constant(*L, *K, p_other->constant_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->constant_map) { + for (const KeyValue<StringName, int> &F : E.value) { + set_constant(F.key, E.key, F.value); } } } // Fonts. { - const StringName *K = nullptr; - while ((K = p_other->font_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->font_map[*K].next(L))) { - set_font(*L, *K, p_other->font_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : p_other->font_map) { + for (const KeyValue<StringName, Ref<Font>> &F : E.value) { + set_font(F.key, E.key, F.value); } } } // Font sizes. { - const StringName *K = nullptr; - while ((K = p_other->font_size_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->font_size_map[*K].next(L))) { - set_font_size(*L, *K, p_other->font_size_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, int>> &E : p_other->font_size_map) { + for (const KeyValue<StringName, int> &F : E.value) { + set_font_size(F.key, E.key, F.value); } } } // Icons. { - const StringName *K = nullptr; - while ((K = p_other->icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->icon_map[*K].next(L))) { - set_icon(*L, *K, p_other->icon_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : p_other->icon_map) { + for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { + set_icon(F.key, E.key, F.value); } } } // Styleboxes. { - const StringName *K = nullptr; - while ((K = p_other->style_map.next(K))) { - const StringName *L = nullptr; - while ((L = p_other->style_map[*K].next(L))) { - set_stylebox(*L, *K, p_other->style_map[*K][*L]); + for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : p_other->style_map) { + for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { + set_stylebox(F.key, E.key, F.value); } } } // Type variations. { - const StringName *K = nullptr; - while ((K = p_other->variation_map.next(K))) { - set_type_variation(*K, p_other->variation_map[*K]); + for (const KeyValue<StringName, StringName> &E : p_other->variation_map) { + set_type_variation(E.key, E.value); } } @@ -1680,12 +1653,10 @@ void Theme::merge_with(const Ref<Theme> &p_other) { void Theme::clear() { // These items need disconnecting. { - const StringName *K = nullptr; - while ((K = icon_map.next(K))) { - const StringName *L = nullptr; - while ((L = icon_map[*K].next(L))) { - Ref<Texture2D> icon = icon_map[*K][*L]; - if (icon.is_valid()) { + for (const KeyValue<StringName, HashMap<StringName, Ref<Texture2D>>> &E : icon_map) { + for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) { + if (F.value.is_valid()) { + Ref<Texture2D> icon = F.value; icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1693,12 +1664,10 @@ void Theme::clear() { } { - const StringName *K = nullptr; - while ((K = style_map.next(K))) { - const StringName *L = nullptr; - while ((L = style_map[*K].next(L))) { - Ref<StyleBox> style = style_map[*K][*L]; - if (style.is_valid()) { + for (const KeyValue<StringName, HashMap<StringName, Ref<StyleBox>>> &E : style_map) { + for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) { + if (F.value.is_valid()) { + Ref<StyleBox> style = F.value; style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } @@ -1706,12 +1675,10 @@ void Theme::clear() { } { - const StringName *K = nullptr; - while ((K = font_map.next(K))) { - const StringName *L = nullptr; - while ((L = font_map[*K].next(L))) { - Ref<Font> font = font_map[*K][*L]; - if (font.is_valid()) { + for (const KeyValue<StringName, HashMap<StringName, Ref<Font>>> &E : font_map) { + for (const KeyValue<StringName, Ref<Font>> &F : E.value) { + if (F.value.is_valid()) { + Ref<Font> font = F.value; font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } } diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 9afe05007d..f8f1e95634 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -137,6 +137,9 @@ public: static Ref<Font> get_fallback_font(); static int get_fallback_font_size(); + static bool is_valid_type_name(const String &p_name); + static bool is_valid_item_name(const String &p_name); + void set_default_base_scale(float p_base_scale); float get_default_base_scale() const; bool has_default_base_scale() const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index da9e1ef2f6..cee3f85c32 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,8 +1368,8 @@ 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()) { + RBSet<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; + for (RBSet<TileMapCell>::Element *E = set.front(); E; E = E->next()) { if (E->get().source_id >= 0) { Ref<TileSetSource> source = sources[E->get().source_id]; Ref<TileSetAtlasSource> atlas_source = source; @@ -1389,7 +1389,7 @@ 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()) { + for (RBSet<TileMapCell>::Element *E = set.front(); E; E = E->next()) { if (E->get().source_id >= 0) { Ref<TileSetSource> source = sources[E->get().source_id]; @@ -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))); @@ -3264,16 +3264,10 @@ void TileSet::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); ADD_ARRAY("occlusion_layers", "occlusion_layer_"); - ADD_GROUP("Physics", ""); + ADD_GROUP("", ""); ADD_ARRAY("physics_layers", "physics_layer_"); - - ADD_GROUP("Terrains", ""); ADD_ARRAY("terrain_sets", "terrain_set_"); - - ADD_GROUP("Navigation", ""); ADD_ARRAY("navigation_layers", "navigation_layer_"); - - ADD_GROUP("Custom Data", ""); ADD_ARRAY("custom_data_layers", "custom_data_layer_"); // -- Enum binding -- 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 129f76702e..8e7824665f 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -74,6 +74,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = pv.x; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = pv.x; + } break; default: break; } @@ -94,6 +98,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = (int)pv.x; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = (int)pv.x; + } break; default: break; } @@ -115,6 +123,10 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p Vector3 pv = p_prev_value; value = Vector2(pv.x, pv.y); } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = Vector2(pv.x, pv.y); + } break; default: break; } @@ -136,6 +148,35 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p case Variant::VECTOR3: { value = p_prev_value; } break; + case Variant::QUATERNION: { + Quaternion pv = p_prev_value; + value = Vector3(pv.x, pv.y, pv.z); + } break; + default: + break; + } + } break; + case Variant::QUATERNION: { + switch (p_prev_value.get_type()) { + case Variant::INT: { + float pv = (float)(int)p_prev_value; + value = Quaternion(pv, pv, pv, pv); + } break; + case Variant::FLOAT: { + float pv = p_prev_value; + value = Quaternion(pv, pv, pv, pv); + } break; + case Variant::VECTOR2: { + Vector2 pv = p_prev_value; + value = Quaternion(pv.x, pv.y, pv.y, pv.y); + } break; + case Variant::VECTOR3: { + Vector3 pv = p_prev_value; + value = Quaternion(pv.x, pv.y, pv.z, pv.z); + } break; + case Variant::QUATERNION: { + value = p_prev_value; + } break; default: break; } @@ -253,6 +294,9 @@ int VisualShaderNode::get_expanded_output_port_count() const { case PORT_TYPE_VECTOR_3D: { count2 += 3; } break; + case PORT_TYPE_VECTOR_4D: { + count2 += 4; + } break; default: break; } @@ -301,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 { @@ -360,6 +404,7 @@ void VisualShaderNode::_bind_methods() { BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT); BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(PORT_TYPE_BOOLEAN); BIND_ENUM_CONSTANT(PORT_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(PORT_TYPE_SAMPLER); @@ -767,7 +812,6 @@ void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, co if (input.is_valid()) { input->shader_mode = shader_mode; input->shader_type = p_type; - input->connect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed), varray(p_type, p_id)); } n.node->connect("changed", callable_mp(this, &VisualShader::_queue_update)); @@ -837,11 +881,6 @@ void VisualShader::remove_node(Type p_type, int p_id) { Graph *g = &graph[p_type]; ERR_FAIL_COND(!g->nodes.has(p_id)); - Ref<VisualShaderNodeInput> input = g->nodes[p_id].node; - if (input.is_valid()) { - input->disconnect("input_type_changed", callable_mp(this, &VisualShader::_input_type_changed)); - } - g->nodes[p_id].node->disconnect("changed", callable_mp(this, &VisualShader::_queue_update)); g->nodes.erase(p_id); @@ -1139,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"; @@ -1183,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()); @@ -1203,6 +1242,9 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port case VisualShaderNode::PORT_TYPE_VECTOR_3D: { code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ".xyz;\n"; + } break; default: { code += " COLOR.rgb = vec3(0.0);\n"; } break; @@ -1508,8 +1550,8 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "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)); @@ -1520,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; @@ -1537,7 +1579,7 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value)); } - for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { + for (RBSet<String>::Element *E = toggles.front(); E; E = E->next()) { p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get())); } @@ -1569,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()) { @@ -1687,11 +1729,14 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui inputs[i] = "(" + src_var + " ? 1.0 : 0.0)"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_2D: { - inputs[i] = "dot(" + src_var + ", vec2(0.333333, 0.333333))"; + inputs[i] = "dot(" + src_var + ", vec2(0.5, 0.5))"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "dot(" + src_var + ", vec4(0.25, 0.25, 0.25, 0.25))"; + } break; default: break; } @@ -1705,11 +1750,14 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui inputs[i] = "(" + src_var + " ? 1 : 0)"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_2D: { - inputs[i] = "dot(float(" + src_var + "), vec2(0.333333, 0.333333))"; + inputs[i] = "dot(float(" + src_var + "), vec2(0.5, 0.5))"; } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "dot(float(" + src_var + "), vec4(0.25, 0.25, 0.25, 0.25))"; + } break; default: break; } @@ -1728,6 +1776,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { inputs[i] = "all(bvec3(" + src_var + "))"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "all(bvec4(" + src_var + "))"; + } break; default: break; } @@ -1743,7 +1794,8 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_BOOLEAN: { inputs[i] = "vec2(" + src_var + " ? 1.0 : 0.0)"; } break; - case VisualShaderNode::PORT_TYPE_VECTOR_3D: { + case VisualShaderNode::PORT_TYPE_VECTOR_3D: + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { inputs[i] = "vec2(" + src_var + ".xy)"; } break; default: @@ -1765,6 +1817,30 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_2D: { inputs[i] = "vec3(" + src_var + ", 0.0)"; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + inputs[i] = "vec3(" + src_var + ".xyz)"; + } break; + default: + break; + } + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + switch (out_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: { + inputs[i] = "vec4(" + src_var + ")"; + } break; + case VisualShaderNode::PORT_TYPE_SCALAR_INT: { + inputs[i] = "vec4(float(" + src_var + "))"; + } break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: { + inputs[i] = "vec4(" + src_var + " ? 1.0 : 0.0)"; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_2D: { + inputs[i] = "vec4(" + src_var + ", 0.0, 0.0)"; + } break; + case VisualShaderNode::PORT_TYPE_VECTOR_3D: { + inputs[i] = "vec4(" + src_var + ", 0.0)"; + } break; default: break; } @@ -1799,6 +1875,10 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui Vector3 val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); node_code += " vec3 " + inputs[i] + " = " + vformat("vec3(%.5f, %.5f, %.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::QUATERNION) { + Quaternion val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + node_code += " vec4 " + inputs[i] + " = " + vformat("vec4(%.5f, %.5f, %.5f, %.5f);\n", val.x, val.y, val.z, val.w); } else if (defval.get_type() == Variant::TRANSFORM3D) { Transform3D val = defval; val.basis.transpose(); @@ -1823,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; @@ -1838,6 +1918,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { output_count += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + output_count += 4; + } break; default: break; } @@ -1865,6 +1948,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: outputs[i] = "vec3 " + var_name; break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + outputs[i] = "vec4 " + var_name; + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: outputs[i] = "bool " + var_name; break; @@ -1882,6 +1968,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { j += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + j += 4; + } break; default: break; } @@ -1904,6 +1993,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: code += " vec3 " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: + code += " vec4 " + outputs[i] + ";\n"; + break; case VisualShaderNode::PORT_TYPE_BOOLEAN: code += " bool " + outputs[i] + ";\n"; break; @@ -1921,6 +2013,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui case VisualShaderNode::PORT_TYPE_VECTOR_3D: { j += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + j += 4; + } break; default: break; } @@ -1932,29 +2027,19 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui if (!node_code.is_empty()) { code += node_name; code += node_code; - code += "\n"; } for (int i = 0; i < output_count; i++) { - bool new_line_inserted = false; if (expanded_output_ports[i]) { switch (vsnode->get_output_port_type(i)) { case VisualShaderNode::PORT_TYPE_VECTOR_2D: { if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component - if (!new_line_inserted) { - code += "\n"; - new_line_inserted = true; - } String r = "n_out" + itos(node) + "p" + itos(i + 1); code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n"; outputs[i + 1] = r; } if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component - if (!new_line_inserted) { - code += "\n"; - new_line_inserted = true; - } String g = "n_out" + itos(node) + "p" + itos(i + 2); code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n"; outputs[i + 2] = g; @@ -1964,30 +2049,18 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui } break; case VisualShaderNode::PORT_TYPE_VECTOR_3D: { if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component - if (!new_line_inserted) { - code += "\n"; - new_line_inserted = true; - } String r = "n_out" + itos(node) + "p" + itos(i + 1); code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n"; outputs[i + 1] = r; } if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component - if (!new_line_inserted) { - code += "\n"; - new_line_inserted = true; - } String g = "n_out" + itos(node) + "p" + itos(i + 2); code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n"; outputs[i + 2] = g; } if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component - if (!new_line_inserted) { - code += "\n"; - new_line_inserted = true; - } String b = "n_out" + itos(node) + "p" + itos(i + 3); code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n"; outputs[i + 3] = b; @@ -1995,12 +2068,43 @@ Error VisualShader::_write_node(Type type, StringBuilder *global_code, StringBui i += 3; } break; + case VisualShaderNode::PORT_TYPE_VECTOR_4D: { + if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component + String r = "n_out" + itos(node) + "p" + itos(i + 1); + code += " float " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n"; + outputs[i + 1] = r; + } + + if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component + String g = "n_out" + itos(node) + "p" + itos(i + 2); + code += " float " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n"; + outputs[i + 2] = g; + } + + if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component + String b = "n_out" + itos(node) + "p" + itos(i + 3); + code += " float " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n"; + outputs[i + 3] = b; + } + + if (vsnode->is_output_port_connected(i + 4) || (for_preview && vsnode->get_output_port_for_preview() == (i + 4))) { // alpha-component + String a = "n_out" + itos(node) + "p" + itos(i + 4); + code += " float " + a + " = n_out" + itos(node) + "p" + itos(i) + ".a;\n"; + outputs[i + 4] = a; + } + + i += 4; + } break; default: break; } } } + if (!node_code.is_empty()) { + code += "\n"; + } + code += "\n"; // processed.insert(node); @@ -2029,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"; @@ -2077,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"; @@ -2097,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); } } } @@ -2147,6 +2251,9 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_VECTOR_3D: global_code += "vec3 "; break; + case VaryingType::VARYING_TYPE_VECTOR_4D: + global_code += "vec4 "; + break; case VaryingType::VARYING_TYPE_COLOR: global_code += "vec4 "; break; @@ -2162,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])) { @@ -2175,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) { @@ -2207,6 +2314,9 @@ void VisualShader::_update_shader() const { case VaryingType::VARYING_TYPE_VECTOR_3D: code2 += "vec3(0.0)"; break; + case VaryingType::VARYING_TYPE_VECTOR_4D: + code2 += "vec4(0.0)"; + break; case VaryingType::VARYING_TYPE_COLOR: code2 += "vec4(0.0)"; break; @@ -2447,21 +2557,6 @@ void VisualShader::_queue_update() { call_deferred(SNAME("_update_shader")); } -void VisualShader::_input_type_changed(Type p_type, int p_id) { - ERR_FAIL_INDEX(p_type, TYPE_MAX); - //erase connections using this input, as type changed - Graph *g = &graph[p_type]; - - for (List<Connection>::Element *E = g->connections.front(); E;) { - List<Connection>::Element *N = E->next(); - if (E->get().from_node == p_id) { - g->connections.erase(E); - g->nodes[E->get().to_node].prev_connected_nodes.erase(p_id); - } - E = N; - } -} - void VisualShader::rebuild() { dirty.set(); _update_shader(); @@ -2527,6 +2622,7 @@ void VisualShader::_bind_methods() { BIND_ENUM_CONSTANT(VARYING_TYPE_FLOAT); BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(VARYING_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(VARYING_TYPE_COLOR); BIND_ENUM_CONSTANT(VARYING_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(VARYING_TYPE_MAX); @@ -2569,12 +2665,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "modelview_matrix", "MODELVIEW_MATRIX" }, @@ -2590,7 +2684,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, // Node3D, Fragment - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" }, @@ -2598,8 +2692,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, @@ -2619,7 +2712,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "view_right", "VIEW_RIGHT" }, // Node3D, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, @@ -2647,8 +2740,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_matrix", "MODEL_MATRIX" }, @@ -2656,16 +2748,14 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen_matrix", "SCREEN_MATRIX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "instance_custom", "INSTANCE_CUSTOM" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "vertex_id", "VERTEX_ID" }, // Canvas Item, Fragment - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_pixel_size", "SCREEN_PIXEL_SIZE" }, @@ -2675,42 +2765,34 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, // Canvas Item, Light - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light", "LIGHT" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "light_color", "LIGHT_COLOR" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_position", "LIGHT_POSITION" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "shadow", "SHADOW_MODULATE.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "shadow", "SHADOW_MODULATE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "specular_shininess", "SPECULAR_SHININESS" }, // Particles, Start { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2720,13 +2802,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Start (Custom) { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2736,13 +2816,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Process { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2752,13 +2830,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { // Particles, Process (Custom) { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2770,13 +2846,11 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "collision_normal", "COLLISION_NORMAL" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "COLOR" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" }, - { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_4D, "custom", "CUSTOM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, @@ -2789,8 +2863,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "eyedir", "EYEDIR" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "half_res_color", "HALF_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "half_res_color", "HALF_RES_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_color", "LIGHT0_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_direction", "LIGHT0_DIRECTION" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" }, @@ -2808,8 +2881,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "position", "POSITION" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "quarter_res_color", "QUARTER_RES_COLOR.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "quarter_res_color", "QUARTER_RES_COLOR" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "sky_coords", "SKY_COORDS" }, @@ -2835,20 +2907,20 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Spatial, Fragment - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" }, @@ -2856,7 +2928,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { // Spatial, Light - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" }, @@ -2867,25 +2939,25 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Canvas Item, Fragment - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec3(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, // Canvas Item, Light - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "fragcoord", "FRAGCOORD" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "color", "vec4(1.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, @@ -2927,7 +2999,7 @@ int VisualShaderNodeInput::get_output_port_count() const { } VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const { - return get_input_type_by_name(input_name); + return p_port == 0 ? get_input_type_by_name(input_name) : PORT_TYPE_SCALAR; } String VisualShaderNodeInput::get_output_port_name(int p_port) const { @@ -2938,6 +3010,22 @@ String VisualShaderNodeInput::get_caption() const { return "Input"; } +bool VisualShaderNodeInput::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + switch (get_input_type_by_name(input_name)) { + case PORT_TYPE_VECTOR_2D: + return true; + case PORT_TYPE_VECTOR_3D: + return true; + case PORT_TYPE_VECTOR_4D: + return true; + default: + return false; + } + } + return false; +} + String VisualShaderNodeInput::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) const { if (get_output_port_type(0) == PORT_TYPE_SAMPLER) { return ""; @@ -2970,6 +3058,9 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T case PORT_TYPE_VECTOR_3D: { code = " " + p_output_vars[0] + " = vec3(0.0);\n"; } break; + case PORT_TYPE_VECTOR_4D: { + code = " " + p_output_vars[0] + " = vec4(0.0);\n"; + } break; case PORT_TYPE_BOOLEAN: { code = " " + p_output_vars[0] + " = false;\n"; } break; @@ -3186,6 +3277,8 @@ int VisualShaderNodeUniformRef::get_output_port_count() const { return 1; case UniformType::UNIFORM_TYPE_VECTOR3: return 1; + case UniformType::UNIFORM_TYPE_VECTOR4: + return 1; case UniformType::UNIFORM_TYPE_TRANSFORM: return 1; case UniformType::UNIFORM_TYPE_COLOR: @@ -3210,6 +3303,8 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port return PortType::PORT_TYPE_VECTOR_2D; case UniformType::UNIFORM_TYPE_VECTOR3: return PortType::PORT_TYPE_VECTOR_3D; + case UniformType::UNIFORM_TYPE_VECTOR4: + return PortType::PORT_TYPE_VECTOR_4D; case UniformType::UNIFORM_TYPE_TRANSFORM: return PortType::PORT_TYPE_TRANSFORM; case UniformType::UNIFORM_TYPE_COLOR: @@ -3239,6 +3334,8 @@ String VisualShaderNodeUniformRef::get_output_port_name(int p_port) const { return ""; case UniformType::UNIFORM_TYPE_VECTOR3: return ""; + case UniformType::UNIFORM_TYPE_VECTOR4: + return ""; case UniformType::UNIFORM_TYPE_TRANSFORM: return ""; case UniformType::UNIFORM_TYPE_COLOR: @@ -3311,6 +3408,8 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b return PORT_TYPE_VECTOR_2D; case UniformType::UNIFORM_TYPE_VECTOR3: return PORT_TYPE_VECTOR_3D; + case UniformType::UNIFORM_TYPE_VECTOR4: + return PORT_TYPE_VECTOR_4D; case UniformType::UNIFORM_TYPE_TRANSFORM: return PORT_TYPE_TRANSFORM; case UniformType::UNIFORM_TYPE_COLOR: @@ -3380,94 +3479,89 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { //////////////////////////////////////////////////////////////////////// // Node3D, Vertex. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV2", "UV2" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "Model View Matrix", "MODELVIEW_MATRIX" }, //////////////////////////////////////////////////////////////////////// // Node3D, Fragment. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_roughness", "CLEARCOAT_ROUGHNESS" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" }, - - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_hash_scale", "ALPHA_HASH_SCALE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_aa_edge", "ALPHA_ANTIALIASING_EDGE" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "alpha_uv", "ALPHA_TEXTURE_COORDINATE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Metallic", "METALLIC" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Specular", "SPECULAR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO", "AO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "AO Light Affect", "AO_LIGHT_AFFECT" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim", "RIM" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Rim Tint", "RIM_TINT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat", "CLEARCOAT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Clearcoat Roughness", "CLEARCOAT_ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Anisotropy", "ANISOTROPY" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Anisotropy Flow", "ANISOTROPY_FLOW" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Subsurf Scatter", "SSS_STRENGTH" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Backlight", "BACKLIGHT" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Scissor Threshold", "ALPHA_SCISSOR_THRESHOLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha Hash Scale", "ALPHA_HASH_SCALE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha AA Edge", "ALPHA_ANTIALIASING_EDGE" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Alpha UV", "ALPHA_TEXTURE_COORDINATE" }, //////////////////////////////////////////////////////////////////////// // Node3D, Light. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" }, - { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Diffuse", "DIFFUSE_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Specular", "SPECULAR_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, //////////////////////////////////////////////////////////////////////// // Canvas Item. //////////////////////////////////////////////////////////////////////// // Canvas Item, Vertex. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Vertex", "VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "UV", "UV" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "Point Size", "POINT_SIZE" }, //////////////////////////////////////////////////////////////////////// // Canvas Item, Fragment. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "shadow_vertex", "SHADOW_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Color", "COLOR" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Normal Map", "NORMAL_MAP" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "Normal Map Depth", "NORMAL_MAP_DEPTH" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Light Vertex", "LIGHT_VERTEX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "Shadow Vertex", "SHADOW_VERTEX" }, //////////////////////////////////////////////////////////////////////// // Canvas Item, Light. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" }, - { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Light", "LIGHT" }, //////////////////////////////////////////////////////////////////////// // Sky, Sky. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fog", "FOG.rgb" }, - { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Color", "COLOR" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "Alpha", "ALPHA" }, + { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_4D, "Fog", "FOG" }, //////////////////////////////////////////////////////////////////////// // Fog, Fog. //////////////////////////////////////////////////////////////////////// - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" }, - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" }, - { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "Density", "DENSITY" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Albedo", "ALBEDO" }, + { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "Emission", "EMISSION" }, //////////////////////////////////////////////////////////////////////// { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr }, @@ -3511,7 +3605,7 @@ String VisualShaderNodeOutput::get_input_port_name(int p_port) const { while (ports[idx].mode != Shader::MODE_MAX) { if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { if (count == p_port) { - return String(ports[idx].name).capitalize(); + return String(ports[idx].name); } count++; } @@ -3544,7 +3638,7 @@ bool VisualShaderNodeOutput::is_port_separator(int p_index) const { } if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { String name = get_input_port_name(p_index); - return bool(name == "Ao" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold"); + return bool(name == "AO" || name == "Normal" || name == "Rim" || name == "Clearcoat" || name == "Anisotropy" || name == "Subsurf Scatter" || name == "Alpha Scissor Threshold"); } return false; } @@ -3697,6 +3791,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T incompatible_type = true; } } break; + case RS::GLOBAL_VAR_TYPE_VEC4: { + if (!Object::cast_to<VisualShaderNodeVec4Uniform>(this)) { + incompatible_type = true; + } + } break; case RS::GLOBAL_VAR_TYPE_TRANSFORM: { if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) { incompatible_type = true; @@ -4404,6 +4503,9 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad case PORT_TYPE_VECTOR_3D: tk = "vec3(0.0, 0.0, 0.0)"; break; + case PORT_TYPE_VECTOR_4D: + tk = "vec4(0.0, 0.0, 0.0, 0.0)"; + break; case PORT_TYPE_BOOLEAN: tk = "false"; break; @@ -4542,6 +4644,8 @@ String VisualShaderNodeVarying::get_type_str() const { return "vec2"; case VisualShader::VARYING_TYPE_VECTOR_3D: return "vec3"; + case VisualShader::VARYING_TYPE_VECTOR_4D: + return "vec4"; case VisualShader::VARYING_TYPE_COLOR: return "vec4"; case VisualShader::VARYING_TYPE_TRANSFORM: @@ -4558,6 +4662,8 @@ VisualShaderNodeVarying::PortType VisualShaderNodeVarying::get_port_type(VisualS return PORT_TYPE_VECTOR_2D; case VisualShader::VARYING_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case VisualShader::VARYING_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case VisualShader::VARYING_TYPE_COLOR: if (p_port == 1) { break; // scalar @@ -4718,6 +4824,9 @@ String VisualShaderNodeVaryingGetter::generate_code(Shader::Mode p_mode, VisualS case VisualShader::VARYING_TYPE_VECTOR_3D: from = "vec3(0.0)"; break; + case VisualShader::VARYING_TYPE_VECTOR_4D: + from = "vec4(0.0)"; + break; case VisualShader::VARYING_TYPE_COLOR: from = "vec3(0.0)"; from2 = "0.0"; diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h index 2d4b2852e9..aa5263943b 100644 --- a/scene/resources/visual_shader.h +++ b/scene/resources/visual_shader.h @@ -83,6 +83,7 @@ public: VARYING_TYPE_FLOAT, VARYING_TYPE_VECTOR_2D, VARYING_TYPE_VECTOR_3D, + VARYING_TYPE_VECTOR_4D, VARYING_TYPE_COLOR, VARYING_TYPE_TRANSFORM, VARYING_TYPE_MAX, @@ -126,7 +127,7 @@ private: }; struct Graph { - Map<int, Node> nodes; + RBMap<int, Node> nodes; List<Connection> connections; } graph[TYPE_MAX]; @@ -138,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; @@ -157,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; @@ -254,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; @@ -271,6 +272,7 @@ public: PORT_TYPE_SCALAR_INT, PORT_TYPE_VECTOR_2D, PORT_TYPE_VECTOR_3D, + PORT_TYPE_VECTOR_4D, PORT_TYPE_BOOLEAN, PORT_TYPE_TRANSFORM, PORT_TYPE_SAMPLER, @@ -326,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; @@ -446,6 +448,7 @@ public: virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + virtual bool is_output_port_expandable(int p_port) const override; virtual String get_caption() const override; @@ -556,6 +559,7 @@ public: UNIFORM_TYPE_BOOLEAN, UNIFORM_TYPE_VECTOR2, UNIFORM_TYPE_VECTOR3, + UNIFORM_TYPE_VECTOR4, UNIFORM_TYPE_TRANSFORM, UNIFORM_TYPE_COLOR, UNIFORM_TYPE_SAMPLER, @@ -676,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 4e16353460..dbd45793f9 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -38,6 +38,8 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_ return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -50,6 +52,8 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_output_port return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -73,10 +77,11 @@ void VisualShaderNodeVectorBase::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeVectorBase::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeVectorBase::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -291,7 +296,7 @@ int VisualShaderNodeColorConstant::get_input_port_count() const { } VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; + return PORT_TYPE_VECTOR_4D; } String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { @@ -299,15 +304,15 @@ String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { } int VisualShaderNodeColorConstant::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const { - return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port + return ""; } bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const { @@ -318,11 +323,7 @@ bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const } String VisualShaderNodeColorConstant::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) const { - String code; - code += " " + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n"; - code += " " + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n"; - - return code; + return " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.r, constant.g, constant.b, constant.a) + ";\n"; } void VisualShaderNodeColorConstant::set_constant(const Color &p_constant) { @@ -477,6 +478,68 @@ void VisualShaderNodeVec3Constant::_bind_methods() { VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() { } +////////////// Vector4 + +String VisualShaderNodeVec4Constant::get_caption() const { + return "Vector4Constant"; +} + +int VisualShaderNodeVec4Constant::get_input_port_count() const { + return 0; +} + +VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Constant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec4Constant::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVec4Constant::PortType VisualShaderNodeVec4Constant::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Constant::get_output_port_name(int p_port) const { + return ""; // No output port means the editor will be used as port. +} + +String VisualShaderNodeVec4Constant::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) const { + return " " + p_output_vars[0] + " = " + vformat("vec4(%.6f, %.6f, %.6f, %.6f)", constant.x, constant.y, constant.z, constant.w) + ";\n"; +} + +void VisualShaderNodeVec4Constant::set_constant(const Quaternion &p_constant) { + if (constant.is_equal_approx(p_constant)) { + return; + } + constant = p_constant; + emit_changed(); +} + +Quaternion VisualShaderNodeVec4Constant::get_constant() const { + return constant; +} + +Vector<StringName> VisualShaderNodeVec4Constant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeVec4Constant::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_constant", "constant"), &VisualShaderNodeVec4Constant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec4Constant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeVec4Constant::VisualShaderNodeVec4Constant() { +} + ////////////// Transform3D String VisualShaderNodeTransformConstant::get_caption() const { @@ -584,21 +647,25 @@ String VisualShaderNodeTexture::get_input_port_name(int p_port) const { } int VisualShaderNodeTexture::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const { - if (p_port == 0 && source == SOURCE_DEPTH) { - return PORT_TYPE_SCALAR; + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_4D; + default: + return PORT_TYPE_SCALAR; } - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; } String VisualShaderNodeTexture::get_output_port_name(int p_port) const { - if (p_port == 0 && source == SOURCE_DEPTH) { - return "depth"; + switch (p_port) { + case 0: + return "color"; + default: + return ""; } - return p_port == 0 ? "rgb" : "alpha"; } bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const { @@ -655,170 +722,116 @@ String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader: default_uv = "vec2(0.0)"; } + String code; if (source == SOURCE_TEXTURE) { String id = make_unique_id(p_type, p_id, "tex"); - String code; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; return code; } if (source == SOURCE_PORT) { String id = p_input_vars[2]; - - String code; - code += " {\n"; if (id.is_empty()) { - code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; } else { if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; } - code += " }\n"; return code; } if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { - String code = " {\n"; if (p_input_vars[0].is_empty() || p_for_preview) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0 );\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", 0.0);\n"; } else { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", 0.0);\n"; } else { - code += " vec4 _tex_read = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(SCREEN_TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = _tex_read.a;\n"; - code += " }\n"; return code; } if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - String code = " {\n"; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = texture(TEXTURE, " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(TEXTURE, " + default_uv + ");\n"; } else { - code += " vec4 _tex_read = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = texture(TEXTURE, " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(TEXTURE, " + p_input_vars[0] + ");\n"; } else { - code += " vec4 _tex_read = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(TEXTURE, " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = _tex_read.a;\n"; - code += " }\n"; return code; } if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { - String code = " {\n"; if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + default_uv + ");\n"; } else { - code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ");\n"; } - } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 _tex_read = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; + code += " " + p_output_vars[0] + " = texture(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy);\n"; } else { - code += " vec4 _tex_read = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(NORMAL_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ");\n"; } - - code += " " + p_output_vars[0] + " = _tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = _tex_read.a;\n"; - code += " }\n"; return code; } - if (p_for_preview) // DEPTH_TEXTURE is not supported in preview(canvas_item) shader - { - if (source == SOURCE_DEPTH) { - String code; - code += " " + p_output_vars[0] + " = 0.0;\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; - return code; - } - } - - if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { - String code = " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. - - if (p_input_vars[1].is_empty()) { - code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; + if (source == SOURCE_DEPTH) { + if (!p_for_preview && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) { + code += " {\n"; + if (p_input_vars[0].is_empty()) { // Use UV by default. + if (p_input_vars[1].is_empty()) { + code += " float _depth = texture(DEPTH_TEXTURE, " + default_uv + ").r;\n"; + } else { + code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + } + } else if (p_input_vars[1].is_empty()) { + //no lod + code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; } else { - code += " float _depth = textureLod(DEPTH_TEXTURE, " + default_uv + ", " + p_input_vars[1] + ").r;\n"; + code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " float _depth = texture(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy).r;\n"; - } else { - code += " float _depth = textureLod(DEPTH_TEXTURE, " + p_input_vars[0] + ".xy, " + p_input_vars[1] + ").r;\n"; + code += " " + p_output_vars[0] + " = vec4(_depth, _depth, _depth, 1.0);\n"; + code += " }\n"; + return code; } - - code += " " + p_output_vars[0] + " = _depth;\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; - code += " }\n"; - return code; - } else if (source == SOURCE_DEPTH) { - String code; - code += " " + p_output_vars[0] + " = 0.0;\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; - return code; } - //none - String code; - code += " " + p_output_vars[0] + " = vec3(0.0);\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } @@ -1157,15 +1170,15 @@ String VisualShaderNodeSample3D::get_input_port_name(int p_port) const { } int VisualShaderNodeSample3D::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeSample3D::get_output_port_name(int p_port) const { - return p_port == 0 ? "rgb" : "alpha"; + return "color"; } bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const { @@ -1195,7 +1208,6 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader String code; if (source == SOURCE_TEXTURE || source == SOURCE_PORT) { String id; - code += " {\n"; if (source == SOURCE_TEXTURE) { id = make_unique_id(p_type, p_id, "tex3d"); } else { @@ -1204,27 +1216,22 @@ String VisualShaderNodeSample3D::generate_code(Shader::Mode p_mode, VisualShader if (!id.is_empty()) { if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } } else { - code += " vec4 " + id + "_tex_read = vec4(0.0);\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; } - - code += " " + p_output_vars[0] + " = " + id + "_tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_tex_read.a;\n"; - code += " }\n"; return code; } - code += " " + p_output_vars[0] + " = vec3(0.0);\n"; - code += " " + p_output_vars[1] + " = 1.0;\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } @@ -1422,15 +1429,15 @@ String VisualShaderNodeCubemap::get_input_port_name(int p_port) const { } int VisualShaderNodeCubemap::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return PORT_TYPE_VECTOR_4D; } String VisualShaderNodeCubemap::get_output_port_name(int p_port) const { - return p_port == 0 ? "rgb" : "alpha"; + return "color"; } bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const { @@ -1484,37 +1491,28 @@ String VisualShaderNodeCubemap::generate_code(Shader::Mode p_mode, VisualShader: } else if (source == SOURCE_PORT) { id = p_input_vars[2]; } else { - return String(); + return code; } - code += " {\n"; - if (id.is_empty()) { - code += " vec4 " + id + "_read = vec4(0.0);\n"; - code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; - code += " }\n"; + code += " " + p_output_vars[0] + " = vec4(0.0);\n"; return code; } if (p_input_vars[0].is_empty()) { // Use UV by default. if (p_input_vars[1].is_empty()) { - code += " vec4 " + id + "_read = texture(" + id + ", " + default_uv + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + default_uv + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + " );\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; } } else if (p_input_vars[1].is_empty()) { //no lod - code += " vec4 " + id + "_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; + code += " " + p_output_vars[0] + " = texture(" + id + ", " + p_input_vars[0] + ");\n"; } else { - code += " vec4 " + id + "_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - code += " " + p_output_vars[0] + " = " + id + "_read.rgb;\n"; - code += " " + p_output_vars[1] + " = " + id + "_read.a;\n"; - code += " }\n"; - return code; } @@ -1891,8 +1889,10 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader code += "min(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_CROSS: - if (op_type == OP_TYPE_VECTOR_2D) { // not supported + if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. code += "vec2(0.0);\n"; + } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. + code += "vec4(0.0);\n"; } else { code += "cross(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } @@ -1901,8 +1901,10 @@ String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader code += "atan(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; break; case OP_REFLECT: - if (op_type == OP_TYPE_VECTOR_2D) { // not supported + if (op_type == OP_TYPE_VECTOR_2D) { // Not supported. code += "vec2(0.0);\n"; + } else if (op_type == OP_TYPE_VECTOR_4D) { // Not supported. + code += "vec4(0.0);\n"; } else { code += "reflect(" + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } @@ -1931,6 +1933,10 @@ void VisualShaderNodeVectorOp::set_op_type(OpType p_op_type) { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; default: break; } @@ -1960,7 +1966,7 @@ Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const { String VisualShaderNodeVectorOp::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { bool invalid_type = false; - if (op_type == OP_TYPE_VECTOR_2D) { + if (op_type == OP_TYPE_VECTOR_2D || op_type == OP_TYPE_VECTOR_4D) { if (op == OP_CROSS || op == OP_REFLECT) { invalid_type = true; } @@ -2004,6 +2010,10 @@ VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() { set_input_port_default_value(0, Vector3()); set_input_port_default_value(1, Vector3()); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion()); + set_input_port_default_value(1, Quaternion()); + } break; default: break; } @@ -2598,8 +2608,6 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad "", // FUNC_SATURATE "-($)", "1.0 / ($)", - "", // FUNC_RGB2HSV - "", // FUNC_HSV2RGB "abs($)", "acos($)", "acosh($)", @@ -2636,8 +2644,10 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad if (op_type == OP_TYPE_VECTOR_2D) { code = "max(min($, vec2(1.0)), vec2(0.0))"; - } else { + } else if (op_type == OP_TYPE_VECTOR_3D) { code = "max(min($, vec3(1.0)), vec3(0.0))"; + } else { + code = "max(min($, vec4(1.0)), vec4(0.0))"; } return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n"; } @@ -2646,44 +2656,16 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad String code; if (op_type == OP_TYPE_VECTOR_2D) { - code = "vec2(1.0, 1.0) - $"; + code = "vec2(1.0) - $"; + } else if (op_type == OP_TYPE_VECTOR_3D) { + code = "vec3(1.0) - $"; } else { - code = "vec3(1.0, 1.0, 1.0) - $"; + code = "vec4(1.0) - $"; } return " " + p_output_vars[0] + " = " + code.replace("$", p_input_vars[0]) + ";\n"; } - String code; - - if (func == FUNC_RGB2HSV) { - if (op_type == OP_TYPE_VECTOR_2D) { // not supported - return " " + p_output_vars[0] + " = vec2(0.0);\n"; - } - code += " {\n"; - code += " vec3 c = " + p_input_vars[0] + ";\n"; - code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; - code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; - code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; - code += " float d = q.x - min(q.w, q.y);\n"; - code += " float e = 1.0e-10;\n"; - code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; - code += " }\n"; - } else if (func == FUNC_HSV2RGB) { - if (op_type == OP_TYPE_VECTOR_2D) { // not supported - return " " + p_output_vars[0] + " = vec2(0.0);\n"; - } - code += " {\n"; - code += " vec3 c = " + p_input_vars[0] + ";\n"; - code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; - code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; - code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; - code += " }\n"; - - } else { - code += " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; - } - - return code; + return " " + p_output_vars[0] + " = " + String(funcs[func]).replace("$", p_input_vars[0]) + ";\n"; } void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) { @@ -2698,6 +2680,9 @@ void VisualShaderNodeVectorFunc::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -2710,13 +2695,6 @@ void VisualShaderNodeVectorFunc::set_function(Function p_func) { if (func == p_func) { return; } - if (p_func == FUNC_RGB2HSV) { - simple_decl = false; - } else if (p_func == FUNC_HSV2RGB) { - simple_decl = false; - } else { - simple_decl = true; - } func = p_func; emit_changed(); } @@ -2731,34 +2709,16 @@ Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const { return props; } -String VisualShaderNodeVectorFunc::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { - bool invalid_type = false; - - if (op_type == OP_TYPE_VECTOR_2D) { - if (func == FUNC_RGB2HSV || func == FUNC_HSV2RGB) { - invalid_type = true; - } - } - - if (invalid_type) { - return RTR("Invalid function for that type."); - } - - return String(); -} - void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); BIND_ENUM_CONSTANT(FUNC_NEGATE); BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); - BIND_ENUM_CONSTANT(FUNC_RGB2HSV); - BIND_ENUM_CONSTANT(FUNC_HSV2RGB); BIND_ENUM_CONSTANT(FUNC_ABS); BIND_ENUM_CONSTANT(FUNC_ACOS); BIND_ENUM_CONSTANT(FUNC_ACOSH); @@ -2799,6 +2759,9 @@ VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3()); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion()); + } break; default: break; } @@ -2846,6 +2809,25 @@ String VisualShaderNodeColorFunc::generate_code(Shader::Mode p_mode, VisualShade code += " " + p_output_vars[0] + " = vec3(max2, max2, max2);\n"; code += " }\n"; break; + case FUNC_HSV2RGB: + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; + code += " vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; + code += " " + p_output_vars[0] + " = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; + code += " }\n"; + break; + case FUNC_RGB2HSV: + code += " {\n"; + code += " vec3 c = " + p_input_vars[0] + ";\n"; + code += " vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; + code += " vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; + code += " vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; + code += " float d = q.x - min(q.w, q.y);\n"; + code += " float e = 1.0e-10;\n"; + code += " " + p_output_vars[0] + " = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; + code += " }\n"; + break; case FUNC_SEPIA: code += " {\n"; code += " vec3 c = " + p_input_vars[0] + ";\n"; @@ -2885,9 +2867,11 @@ void VisualShaderNodeColorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeColorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeColorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,Sepia"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Grayscale,HSV2RGB,RGB2HSV,Sepia"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_GRAYSCALE); + BIND_ENUM_CONSTANT(FUNC_HSV2RGB); + BIND_ENUM_CONSTANT(FUNC_RGB2HSV); BIND_ENUM_CONSTANT(FUNC_SEPIA); BIND_ENUM_CONSTANT(FUNC_MAX); } @@ -3189,6 +3173,9 @@ void VisualShaderNodeVectorLen::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -3258,6 +3245,8 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_inp return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3278,6 +3267,8 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_out return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3315,6 +3306,9 @@ void VisualShaderNodeDerivativeFunc::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -3353,12 +3347,13 @@ void VisualShaderNodeDerivativeFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeDerivativeFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeDerivativeFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sum,X,Y"), "set_function", "get_function"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); BIND_ENUM_CONSTANT(FUNC_SUM); @@ -3389,6 +3384,8 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3418,6 +3415,8 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3458,6 +3457,11 @@ void VisualShaderNodeClamp::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + break; default: break; } @@ -3479,12 +3483,13 @@ void VisualShaderNodeClamp::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeClamp::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeClamp::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_FLOAT); BIND_ENUM_CONSTANT(OP_TYPE_INT); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3541,6 +3546,11 @@ void VisualShaderNodeFaceForward::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + } break; default: break; } @@ -3630,6 +3640,13 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_p return PORT_TYPE_VECTOR_3D; } break; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 1) { + return PORT_TYPE_VECTOR_4D; + } + break; default: break; } @@ -3660,6 +3677,10 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_ return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3696,6 +3717,14 @@ void VisualShaderNodeStep::set_op_type(OpType p_op_type) { set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + } break; default: break; } @@ -3721,13 +3750,15 @@ void VisualShaderNodeStep::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeStep::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeStep::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3762,6 +3793,13 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_ return PORT_TYPE_VECTOR_3D; // x } break; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 2) { + return PORT_TYPE_VECTOR_4D; // x + } + break; default: break; } @@ -3794,6 +3832,10 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -3835,6 +3877,16 @@ void VisualShaderNodeSmoothStep::set_op_type(OpType p_op_type) { set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1 set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); // x break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // edge0 + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // edge1 + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x + break; + case OP_TYPE_VECTOR_4D_SCALAR: + set_input_port_default_value(0, 0.0, get_input_port_default_value(0)); // edge0 + set_input_port_default_value(1, 0.0, get_input_port_default_value(1)); // edge1 + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // x + break; default: break; } @@ -3860,13 +3912,15 @@ void VisualShaderNodeSmoothStep::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeSmoothStep::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSmoothStep::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -3922,6 +3976,10 @@ void VisualShaderNodeVectorDistance::set_op_type(OpType p_op_type) { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); // a set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + } break; default: break; } @@ -4016,6 +4074,13 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_por break; } return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + if (p_port == 2) { + break; + } + return PORT_TYPE_VECTOR_4D; default: break; } @@ -4046,6 +4111,10 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_po return PORT_TYPE_VECTOR_3D; case OP_TYPE_VECTOR_3D_SCALAR: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; + case OP_TYPE_VECTOR_4D_SCALAR: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -4087,6 +4156,16 @@ void VisualShaderNodeMix::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); // b set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); // weight + } break; + case OP_TYPE_VECTOR_4D_SCALAR: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); // a + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); // b + set_input_port_default_value(2, 0.0, get_input_port_default_value(2)); // weight + } break; default: break; } @@ -4112,13 +4191,15 @@ void VisualShaderNodeMix::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeMix::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMix::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } @@ -4140,6 +4221,8 @@ int VisualShaderNodeVectorCompose::get_input_port_count() const { return 2; case OP_TYPE_VECTOR_3D: return 3; + case OP_TYPE_VECTOR_4D: + return 4; default: break; } @@ -4170,6 +4253,18 @@ String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const { return "z"; } } break; + case OP_TYPE_VECTOR_4D: { + switch (p_port) { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + } + } break; default: break; } @@ -4205,6 +4300,15 @@ void VisualShaderNodeVectorCompose::set_op_type(OpType p_op_type) { set_input_port_default_value(1, p2); set_input_port_default_value(2, 0.0); } break; + case OP_TYPE_VECTOR_4D: { + float p1 = get_input_port_default_value(0); + float p2 = get_input_port_default_value(1); + + set_input_port_default_value(0, p1); + set_input_port_default_value(1, p2); + set_input_port_default_value(2, 0.0); + set_input_port_default_value(3, 0.0); + } break; default: break; } @@ -4221,6 +4325,9 @@ String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualS case OP_TYPE_VECTOR_3D: { code += " " + p_output_vars[0] + " = vec3(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ");\n"; } break; + case OP_TYPE_VECTOR_4D: { + code += " " + p_output_vars[0] + " = vec4(" + p_input_vars[0] + ", " + p_input_vars[1] + ", " + p_input_vars[2] + ", " + p_input_vars[3] + ");\n"; + } break; default: break; } @@ -4301,6 +4408,8 @@ int VisualShaderNodeVectorDecompose::get_output_port_count() const { return 2; case OP_TYPE_VECTOR_3D: return 3; + case OP_TYPE_VECTOR_4D: + return 4; default: break; } @@ -4331,6 +4440,18 @@ String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const { return "z"; } } break; + case OP_TYPE_VECTOR_4D: { + switch (p_port) { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + } + } break; default: break; } @@ -4349,6 +4470,9 @@ void VisualShaderNodeVectorDecompose::set_op_type(OpType p_op_type) { case OP_TYPE_VECTOR_3D: { set_input_port_default_value(0, Vector3(), get_input_port_default_value(0)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + } break; default: break; } @@ -4368,6 +4492,12 @@ String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, Visua code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; } break; + case OP_TYPE_VECTOR_4D: { + code += " " + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n"; + code += " " + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; + code += " " + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; + code += " " + p_output_vars[3] + " = " + p_input_vars[0] + ".w;\n"; + } break; default: break; } @@ -4932,7 +5062,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const { } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_VECTOR_3D; + return PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { @@ -4940,15 +5070,22 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { } int VisualShaderNodeColorUniform::get_output_port_count() const { - return 2; + return 1; } VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR; + return p_port == 0 ? PORT_TYPE_VECTOR_4D : PORT_TYPE_SCALAR; } String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { - return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port + return "color"; +} + +bool VisualShaderNodeColorUniform::is_output_port_expandable(int p_port) const { + if (p_port == 0) { + return true; + } + return false; } void VisualShaderNodeColorUniform::set_default_value_enabled(bool p_enabled) { @@ -4985,9 +5122,7 @@ String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, Visual } String VisualShaderNodeColorUniform::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) const { - String code = " " + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; - code += " " + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; - return code; + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } bool VisualShaderNodeColorUniform::is_show_prop_names() const { @@ -5225,6 +5360,106 @@ Vector<StringName> VisualShaderNodeVec3Uniform::get_editable_properties() const VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() { } +////////////// Vector4 Uniform + +String VisualShaderNodeVec4Uniform::get_caption() const { + return "Vector4Uniform"; +} + +int VisualShaderNodeVec4Uniform::get_input_port_count() const { + return 0; +} + +VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Uniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec4Uniform::get_output_port_count() const { + return 1; +} + +VisualShaderNodeVec4Uniform::PortType VisualShaderNodeVec4Uniform::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR_4D; +} + +String VisualShaderNodeVec4Uniform::get_output_port_name(int p_port) const { + return ""; // No output port means the editor will be used as port. +} + +void VisualShaderNodeVec4Uniform::set_default_value_enabled(bool p_enabled) { + default_value_enabled = p_enabled; + emit_changed(); +} + +bool VisualShaderNodeVec4Uniform::is_default_value_enabled() const { + return default_value_enabled; +} + +void VisualShaderNodeVec4Uniform::set_default_value(const Quaternion &p_value) { + default_value = p_value; + emit_changed(); +} + +Quaternion VisualShaderNodeVec4Uniform::get_default_value() const { + return default_value; +} + +String VisualShaderNodeVec4Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = _get_qual_str() + "uniform vec4 " + get_uniform_name(); + if (default_value_enabled) { + code += vformat(" = vec4(%.6f, %.6f, %.6f, %.6f)", default_value.x, default_value.y, default_value.z, default_value.w); + } + code += ";\n"; + return code; +} + +String VisualShaderNodeVec4Uniform::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) const { + return " " + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +void VisualShaderNodeVec4Uniform::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_default_value_enabled", "enabled"), &VisualShaderNodeVec4Uniform::set_default_value_enabled); + ClassDB::bind_method(D_METHOD("is_default_value_enabled"), &VisualShaderNodeVec4Uniform::is_default_value_enabled); + + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeVec4Uniform::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeVec4Uniform::get_default_value); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "default_value_enabled"), "set_default_value_enabled", "is_default_value_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "default_value"), "set_default_value", "get_default_value"); +} + +bool VisualShaderNodeVec4Uniform::is_show_prop_names() const { + return true; +} + +bool VisualShaderNodeVec4Uniform::is_use_prop_slots() const { + return true; +} + +bool VisualShaderNodeVec4Uniform::is_qualifier_supported(Qualifier p_qual) const { + return true; // All qualifiers are supported. +} + +bool VisualShaderNodeVec4Uniform::is_convertible_to_constant() const { + return true; // Conversion is allowed. +} + +Vector<StringName> VisualShaderNodeVec4Uniform::get_editable_properties() const { + Vector<StringName> props = VisualShaderNodeUniform::get_editable_properties(); + props.push_back("default_value_enabled"); + if (default_value_enabled) { + props.push_back("default_value"); + } + return props; +} + +VisualShaderNodeVec4Uniform::VisualShaderNodeVec4Uniform() { +} + ////////////// Transform Uniform String VisualShaderNodeTransformUniform::get_caption() const { @@ -5276,9 +5511,9 @@ Transform3D VisualShaderNodeTransformUniform::get_default_value() const { String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform mat4 " + get_uniform_name(); if (default_value_enabled) { - Vector3 row0 = default_value.basis.get_row(0); - Vector3 row1 = default_value.basis.get_row(1); - Vector3 row2 = default_value.basis.get_row(2); + Vector3 row0 = default_value.basis.rows[0]; + Vector3 row1 = default_value.basis.rows[1]; + Vector3 row2 = default_value.basis.rows[2]; Vector3 origin = default_value.origin; code += " = mat4(" + vformat("vec4(%.6f, %.6f, %.6f, 0.0)", row0.x, row0.y, row0.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row1.x, row1.y, row1.z) + vformat(", vec4(%.6f, %.6f, %.6f, 0.0)", row2.x, row2.y, row2.z) + vformat(", vec4(%.6f, %.6f, %.6f, 1.0)", origin.x, origin.y, origin.z) + ")"; } @@ -5339,28 +5574,24 @@ String VisualShaderNodeTextureUniform::get_caption() const { } int VisualShaderNodeTextureUniform::get_input_port_count() const { - return 2; + return 0; } VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { - return p_port == 0 ? PORT_TYPE_VECTOR_2D : PORT_TYPE_SCALAR; + return PORT_TYPE_SCALAR; } String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { - return p_port == 0 ? "uv" : "lod"; + return ""; } int VisualShaderNodeTextureUniform::get_output_port_count() const { - return 3; + return 1; } VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { switch (p_port) { case 0: - return PORT_TYPE_VECTOR_3D; - case 1: - return PORT_TYPE_SCALAR; - case 2: return PORT_TYPE_SAMPLER; default: return PORT_TYPE_SCALAR; @@ -5370,10 +5601,6 @@ VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_out String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { switch (p_port) { case 0: - return "rgb"; - case 1: - return "alpha"; - case 2: return "sampler2D"; default: return ""; @@ -5484,37 +5711,8 @@ String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, Visu return code; } -bool VisualShaderNodeTextureUniform::is_code_generated() const { - return is_output_port_connected(0) || is_output_port_connected(1); // rgb or alpha -} - String VisualShaderNodeTextureUniform::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) const { - String default_uv; - if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - default_uv = "UV"; - } else { - default_uv = "vec2(0.0)"; - } - - String id = get_uniform_name(); - String code = " {\n"; - if (p_input_vars[0].is_empty()) { // Use UV by default. - if (p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = texture(" + id + ", " + default_uv + ");\n"; - } else { - code += " vec4 n_tex_read = textureLod(" + id + ", " + default_uv + ", " + p_input_vars[1] + ");\n"; - } - } else if (p_input_vars[1].is_empty()) { - //no lod - code += " vec4 n_tex_read = texture(" + id + ", " + p_input_vars[0] + ");\n"; - } else { - code += " vec4 n_tex_read = textureLod(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; - } - - code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = n_tex_read.a;\n"; - code += " }\n"; - return code; + return ""; } void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_texture_type) { @@ -5584,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")); @@ -5636,15 +5834,6 @@ void VisualShaderNodeTextureUniform::_bind_methods() { BIND_ENUM_CONSTANT(REPEAT_MAX); } -bool VisualShaderNodeTextureUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - if (p_mode == Shader::MODE_CANVAS_ITEM || p_mode == Shader::MODE_SPATIAL) { - if (p_port == 0) { - return true; - } - } - return false; -} - bool VisualShaderNodeTextureUniform::is_qualifier_supported(Qualifier p_qual) const { switch (p_qual) { case Qualifier::QUAL_NONE: @@ -5664,7 +5853,6 @@ bool VisualShaderNodeTextureUniform::is_convertible_to_constant() const { } VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { - simple_decl = false; } ////////////// Texture Uniform (Triplanar) @@ -5677,7 +5865,7 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const { return 2; } -VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const { +VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const { if (p_port == 0 || p_port == 1) { return PORT_TYPE_VECTOR_3D; } @@ -5693,6 +5881,32 @@ String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port) return ""; } +int VisualShaderNodeTextureUniformTriplanar::get_output_port_count() const { + return 2; +} + +VisualShaderNodeTextureUniformTriplanar::PortType VisualShaderNodeTextureUniformTriplanar::get_output_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR_4D; + case 1: + return PORT_TYPE_SAMPLER; + default: + return PORT_TYPE_SCALAR; + } +} + +String VisualShaderNodeTextureUniformTriplanar::get_output_port_name(int p_port) const { + switch (p_port) { + case 0: + return "color"; + case 1: + return "sampler2D"; + default: + return ""; + } +} + String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const { String code; @@ -5733,22 +5947,18 @@ String VisualShaderNodeTextureUniformTriplanar::generate_global_per_func(Shader: String VisualShaderNodeTextureUniformTriplanar::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) const { String id = get_uniform_name(); - String code = " {\n"; + String code; if (p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, triplanar_pos);\n"; } else if (!p_input_vars[0].is_empty() && p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", triplanar_pos);\n"; } else if (p_input_vars[0].is_empty() && !p_input_vars[1].is_empty()) { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", triplanar_power_normal, " + p_input_vars[1] + ");\n"; } else { - code += " vec4 n_tex_read = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; + code += " " + p_output_vars[0] + " = triplanar_texture(" + id + ", " + p_input_vars[0] + ", " + p_input_vars[1] + ");\n"; } - code += " " + p_output_vars[0] + " = n_tex_read.rgb;\n"; - code += " " + p_output_vars[1] + " = n_tex_read.a;\n"; - code += " }\n"; - return code; } @@ -5770,34 +5980,10 @@ String VisualShaderNodeTexture2DArrayUniform::get_caption() const { return "Texture2DArrayUniform"; } -int VisualShaderNodeTexture2DArrayUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeTexture2DArrayUniform::get_output_port_name(int p_port) const { return "sampler2DArray"; } -int VisualShaderNodeTexture2DArrayUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeTexture2DArrayUniform::PortType VisualShaderNodeTexture2DArrayUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeTexture2DArrayUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeTexture2DArrayUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeTexture2DArrayUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform sampler2DArray " + get_uniform_name(); @@ -5843,34 +6029,10 @@ String VisualShaderNodeTexture3DUniform::get_caption() const { return "Texture3DUniform"; } -int VisualShaderNodeTexture3DUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeTexture3DUniform::get_output_port_name(int p_port) const { return "sampler3D"; } -int VisualShaderNodeTexture3DUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeTexture3DUniform::PortType VisualShaderNodeTexture3DUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeTexture3DUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeTexture3DUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeTexture3DUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform sampler3D " + get_uniform_name(); @@ -5916,34 +6078,10 @@ String VisualShaderNodeCubemapUniform::get_caption() const { return "CubemapUniform"; } -int VisualShaderNodeCubemapUniform::get_output_port_count() const { - return 1; -} - -VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_output_port_type(int p_port) const { - return PORT_TYPE_SAMPLER; -} - String VisualShaderNodeCubemapUniform::get_output_port_name(int p_port) const { return "samplerCube"; } -int VisualShaderNodeCubemapUniform::get_input_port_count() const { - return 0; -} - -VisualShaderNodeCubemapUniform::PortType VisualShaderNodeCubemapUniform::get_input_port_type(int p_port) const { - return PORT_TYPE_SCALAR; -} - -String VisualShaderNodeCubemapUniform::get_input_port_name(int p_port) const { - return ""; -} - -bool VisualShaderNodeCubemapUniform::is_input_port_default(int p_port, Shader::Mode p_mode) const { - return false; -} - String VisualShaderNodeCubemapUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { String code = _get_qual_str() + "uniform samplerCube " + get_uniform_name(); @@ -6080,6 +6218,8 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case OP_TYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case OP_TYPE_TRANSFORM: @@ -6116,6 +6256,8 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(in return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case OP_TYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case OP_TYPE_TRANSFORM: @@ -6152,6 +6294,10 @@ void VisualShaderNodeSwitch::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0), get_input_port_default_value(2)); break; + case OP_TYPE_VECTOR_4D: + set_input_port_default_value(1, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(0.0, 0.0, 0.0, 0.0), get_input_port_default_value(2)); + break; case OP_TYPE_BOOLEAN: set_input_port_default_value(1, true); set_input_port_default_value(2, false); @@ -6181,12 +6327,13 @@ void VisualShaderNodeSwitch::_bind_methods() { // static ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeSwitch::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeSwitch::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_FLOAT); BIND_ENUM_CONSTANT(OP_TYPE_INT); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_BOOLEAN); BIND_ENUM_CONSTANT(OP_TYPE_TRANSFORM); BIND_ENUM_CONSTANT(OP_TYPE_MAX); @@ -6420,6 +6567,8 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i return PORT_TYPE_VECTOR_2D; case CTYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case CTYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; case CTYPE_BOOLEAN: return PORT_TYPE_BOOLEAN; case CTYPE_TRANSFORM: @@ -6514,6 +6663,12 @@ String VisualShaderNodeCompare::generate_code(Shader::Mode p_mode, VisualShader: code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n"; code += " }\n"; } break; + case CTYPE_VECTOR_4D: { + code += " {\n"; + code += " bvec4 _bv = " + String(functions[func]).replace("$", p_input_vars[0] + ", " + p_input_vars[1]) + ";\n"; + code += " " + p_output_vars[0] + " = " + String(conditions[condition]).replace("$", "_bv") + ";\n"; + code += " }\n"; + } break; case CTYPE_BOOLEAN: { if (func > FUNC_NOT_EQUAL) { return " " + p_output_vars[0] + " = false;\n"; @@ -6558,6 +6713,11 @@ void VisualShaderNodeCompare::set_comparison_type(ComparisonType p_comparison_ty set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); simple_decl = false; break; + case CTYPE_VECTOR_4D: + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + simple_decl = false; + break; case CTYPE_BOOLEAN: set_input_port_default_value(0, false); set_input_port_default_value(1, false); @@ -6609,7 +6769,7 @@ Vector<StringName> VisualShaderNodeCompare::get_editable_properties() const { Vector<StringName> props; props.push_back("type"); props.push_back("function"); - if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D) { + if (comparison_type == CTYPE_VECTOR_2D || comparison_type == CTYPE_VECTOR_3D || comparison_type == CTYPE_VECTOR_4D) { props.push_back("condition"); } return props; @@ -6625,7 +6785,7 @@ void VisualShaderNodeCompare::_bind_methods() { ClassDB::bind_method(D_METHOD("set_condition", "condition"), &VisualShaderNodeCompare::set_condition); ClassDB::bind_method(D_METHOD("get_condition"), &VisualShaderNodeCompare::get_condition); - ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Boolean,Transform"), "set_comparison_type", "get_comparison_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Float,Int,Vector2,Vector3,Vector4,Boolean,Transform"), "set_comparison_type", "get_comparison_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "a == b,a != b,a > b,a >= b,a < b,a <= b"), "set_function", "get_function"); ADD_PROPERTY(PropertyInfo(Variant::INT, "condition", PROPERTY_HINT_ENUM, "All,Any"), "set_condition", "get_condition"); @@ -6633,6 +6793,7 @@ void VisualShaderNodeCompare::_bind_methods() { BIND_ENUM_CONSTANT(CTYPE_SCALAR_INT); BIND_ENUM_CONSTANT(CTYPE_VECTOR_2D); BIND_ENUM_CONSTANT(CTYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(CTYPE_VECTOR_4D); BIND_ENUM_CONSTANT(CTYPE_BOOLEAN); BIND_ENUM_CONSTANT(CTYPE_TRANSFORM); BIND_ENUM_CONSTANT(CTYPE_MAX); @@ -6672,6 +6833,8 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_por return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -6699,6 +6862,8 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_po return PORT_TYPE_VECTOR_2D; case OP_TYPE_VECTOR_3D: return PORT_TYPE_VECTOR_3D; + case OP_TYPE_VECTOR_4D: + return PORT_TYPE_VECTOR_4D; default: break; } @@ -6734,6 +6899,11 @@ void VisualShaderNodeMultiplyAdd::set_op_type(OpType p_op_type) { set_input_port_default_value(1, Vector3(), get_input_port_default_value(1)); set_input_port_default_value(2, Vector3(), get_input_port_default_value(2)); } break; + case OP_TYPE_VECTOR_4D: { + set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0)); + set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1)); + set_input_port_default_value(2, Quaternion(), get_input_port_default_value(2)); + } break; default: break; } @@ -6755,11 +6925,12 @@ void VisualShaderNodeMultiplyAdd::_bind_methods() { ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeMultiplyAdd::set_op_type); ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeMultiplyAdd::get_op_type); - ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3,Vector4"), "set_op_type", "get_op_type"); BIND_ENUM_CONSTANT(OP_TYPE_SCALAR); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D); BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D); + BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D); BIND_ENUM_CONSTANT(OP_TYPE_MAX); } diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index eeeb91a3ee..b159d25eba 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -44,6 +44,7 @@ public: enum OpType { OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -280,6 +281,36 @@ public: /////////////////////////////////////// +class VisualShaderNodeVec4Constant : public VisualShaderNodeConstant { + GDCLASS(VisualShaderNodeVec4Constant, VisualShaderNodeConstant); + Quaternion constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) 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; + + void set_constant(const Quaternion &p_constant); + Quaternion get_constant() const; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeVec4Constant(); +}; + +/////////////////////////////////////// + class VisualShaderNodeTransformConstant : public VisualShaderNodeConstant { GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNodeConstant); Transform3D constant; @@ -1008,8 +1039,6 @@ public: FUNC_SATURATE, FUNC_NEGATE, FUNC_RECIPROCAL, - FUNC_RGB2HSV, - FUNC_HSV2RGB, FUNC_ABS, FUNC_ACOS, FUNC_ACOSH, @@ -1064,7 +1093,6 @@ public: Function get_function() const; virtual Vector<StringName> get_editable_properties() const override; - String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const override; VisualShaderNodeVectorFunc(); }; @@ -1081,6 +1109,8 @@ class VisualShaderNodeColorFunc : public VisualShaderNode { public: enum Function { FUNC_GRAYSCALE, + FUNC_HSV2RGB, + FUNC_RGB2HSV, FUNC_SEPIA, FUNC_MAX, }; @@ -1282,6 +1312,7 @@ public: OP_TYPE_INT, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -1324,6 +1355,7 @@ public: OP_TYPE_SCALAR, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; @@ -1427,6 +1459,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1471,6 +1505,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1561,6 +1597,8 @@ public: OP_TYPE_VECTOR_2D_SCALAR, OP_TYPE_VECTOR_3D, OP_TYPE_VECTOR_3D_SCALAR, + OP_TYPE_VECTOR_4D, + OP_TYPE_VECTOR_4D_SCALAR, OP_TYPE_MAX, }; @@ -1883,6 +1921,8 @@ public: virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; + bool is_output_port_expandable(int p_port) const override; + 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; @@ -1990,6 +2030,49 @@ public: /////////////////////////////////////// +class VisualShaderNodeVec4Uniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeVec4Uniform, VisualShaderNodeUniform); + +private: + bool default_value_enabled = false; + Quaternion default_value; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const override; + + virtual int get_input_port_count() const override; + virtual PortType get_input_port_type(int p_port) const override; + virtual String get_input_port_name(int p_port) const override; + + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + + 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 bool is_show_prop_names() const override; + virtual bool is_use_prop_slots() const override; + + void set_default_value_enabled(bool p_enabled); + bool is_default_value_enabled() const; + + void set_default_value(const Quaternion &p_value); + Quaternion get_default_value() const; + + bool is_qualifier_supported(Qualifier p_qual) const override; + bool is_convertible_to_constant() const override; + + virtual Vector<StringName> get_editable_properties() const override; + + VisualShaderNodeVec4Uniform(); +}; + +/////////////////////////////////////// + class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform); @@ -2084,7 +2167,6 @@ public: virtual int get_input_port_count() const override; virtual PortType get_input_port_type(int p_port) const override; virtual String get_input_port_name(int p_port) const override; - virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; virtual int get_output_port_count() const override; virtual PortType get_output_port_type(int p_port) const override; @@ -2093,9 +2175,8 @@ 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; - virtual bool is_code_generated() const override; Vector<StringName> get_editable_properties() const override; @@ -2134,6 +2215,10 @@ public: virtual PortType get_input_port_type(int p_port) const override; virtual String get_input_port_name(int p_port) const override; + virtual int get_output_port_count() const override; + virtual PortType get_output_port_type(int p_port) const override; + virtual String get_output_port_name(int p_port) const override; + virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override; @@ -2150,16 +2235,8 @@ class VisualShaderNodeTexture2DArrayUniform : public VisualShaderNodeTextureUnif public: virtual String get_caption() const override; - - virtual int get_input_port_count() const override; - virtual PortType get_input_port_type(int p_port) const override; - virtual String get_input_port_name(int p_port) const override; - - virtual int get_output_port_count() const override; - virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; - virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; 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; @@ -2173,16 +2250,8 @@ class VisualShaderNodeTexture3DUniform : public VisualShaderNodeTextureUniform { public: virtual String get_caption() const override; - - virtual int get_input_port_count() const override; - virtual PortType get_input_port_type(int p_port) const override; - virtual String get_input_port_name(int p_port) const override; - - virtual int get_output_port_count() const override; - virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; - virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; 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; @@ -2196,16 +2265,8 @@ class VisualShaderNodeCubemapUniform : public VisualShaderNodeTextureUniform { public: virtual String get_caption() const override; - - virtual int get_input_port_count() const override; - virtual PortType get_input_port_type(int p_port) const override; - virtual String get_input_port_name(int p_port) const override; - - virtual int get_output_port_count() const override; - virtual PortType get_output_port_type(int p_port) const override; virtual String get_output_port_name(int p_port) const override; - virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override; 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; @@ -2248,6 +2309,7 @@ public: OP_TYPE_INT, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_BOOLEAN, OP_TYPE_TRANSFORM, OP_TYPE_MAX, @@ -2362,6 +2424,7 @@ public: CTYPE_SCALAR_INT, CTYPE_VECTOR_2D, CTYPE_VECTOR_3D, + CTYPE_VECTOR_4D, CTYPE_BOOLEAN, CTYPE_TRANSFORM, CTYPE_MAX, @@ -2431,6 +2494,7 @@ public: OP_TYPE_SCALAR, OP_TYPE_VECTOR_2D, OP_TYPE_VECTOR_3D, + OP_TYPE_VECTOR_4D, OP_TYPE_MAX, }; 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(); |