summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp13
-rw-r--r--scene/2d/animated_sprite_2d.h2
-rw-r--r--scene/2d/area_2d.cpp104
-rw-r--r--scene/2d/area_2d.h6
-rw-r--r--scene/2d/audio_stream_player_2d.cpp2
-rw-r--r--scene/2d/camera_2d.cpp6
-rw-r--r--scene/2d/canvas_modulate.cpp2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_object_2d.h2
-rw-r--r--scene/2d/collision_polygon_2d.cpp13
-rw-r--r--scene/2d/collision_shape_2d.cpp11
-rw-r--r--scene/2d/cpu_particles_2d.cpp44
-rw-r--r--scene/2d/cpu_particles_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp30
-rw-r--r--scene/2d/gpu_particles_2d.h26
-rw-r--r--scene/2d/joint_2d.cpp10
-rw-r--r--scene/2d/light_2d.cpp2
-rw-r--r--scene/2d/light_occluder_2d.cpp5
-rw-r--r--scene/2d/light_occluder_2d.h5
-rw-r--r--scene/2d/navigation_agent_2d.cpp23
-rw-r--r--scene/2d/navigation_agent_2d.h15
-rw-r--r--scene/2d/navigation_obstacle_2d.cpp19
-rw-r--r--scene/2d/navigation_region_2d.cpp14
-rw-r--r--scene/2d/navigation_region_2d.h2
-rw-r--r--scene/2d/node_2d.cpp20
-rw-r--r--scene/2d/parallax_background.cpp8
-rw-r--r--scene/2d/parallax_layer.cpp2
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/physical_bone_2d.cpp6
-rw-r--r--scene/2d/physical_bone_2d.h2
-rw-r--r--scene/2d/physics_body_2d.cpp99
-rw-r--r--scene/2d/physics_body_2d.h6
-rw-r--r--scene/2d/position_2d.cpp21
-rw-r--r--scene/2d/position_2d.h2
-rw-r--r--scene/2d/ray_cast_2d.h2
-rw-r--r--scene/2d/remote_transform_2d.cpp2
-rw-r--r--scene/2d/shape_cast_2d.cpp2
-rw-r--r--scene/2d/shape_cast_2d.h2
-rw-r--r--scene/2d/skeleton_2d.cpp6
-rw-r--r--scene/2d/tile_map.cpp202
-rw-r--r--scene/2d/tile_map.h38
-rw-r--r--scene/2d/touch_screen_button.cpp4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp2
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.h2
-rw-r--r--scene/3d/area_3d.cpp106
-rw-r--r--scene/3d/area_3d.h8
-rw-r--r--scene/3d/audio_stream_player_3d.cpp8
-rw-r--r--scene/3d/bone_attachment_3d.cpp6
-rw-r--r--scene/3d/camera_3d.cpp16
-rw-r--r--scene/3d/collision_object_3d.cpp6
-rw-r--r--scene/3d/collision_object_3d.h4
-rw-r--r--scene/3d/collision_polygon_3d.cpp4
-rw-r--r--scene/3d/collision_shape_3d.cpp8
-rw-r--r--scene/3d/collision_shape_3d.h3
-rw-r--r--scene/3d/cpu_particles_3d.cpp80
-rw-r--r--scene/3d/cpu_particles_3d.h6
-rw-r--r--scene/3d/decal.cpp6
-rw-r--r--scene/3d/gpu_particles_3d.cpp16
-rw-r--r--scene/3d/gpu_particles_3d.h18
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp31
-rw-r--r--scene/3d/gpu_particles_collision_3d.h8
-rw-r--r--scene/3d/joint_3d.cpp10
-rw-r--r--scene/3d/label_3d.cpp1010
-rw-r--r--scene/3d/label_3d.h240
-rw-r--r--scene/3d/light_3d.cpp6
-rw-r--r--scene/3d/light_3d.h1
-rw-r--r--scene/3d/lightmap_gi.cpp32
-rw-r--r--scene/3d/lightmapper.h6
-rw-r--r--scene/3d/mesh_instance_3d.cpp8
-rw-r--r--scene/3d/mesh_instance_3d.h2
-rw-r--r--scene/3d/navigation_agent_3d.cpp32
-rw-r--r--scene/3d/navigation_agent_3d.h22
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp18
-rw-r--r--scene/3d/navigation_region_3d.cpp23
-rw-r--r--scene/3d/navigation_region_3d.h6
-rw-r--r--scene/3d/node_3d.cpp10
-rw-r--r--scene/3d/node_3d.h1
-rw-r--r--scene/3d/occluder_instance_3d.cpp10
-rw-r--r--scene/3d/path_3d.cpp8
-rw-r--r--scene/3d/physics_body_3d.cpp72
-rw-r--r--scene/3d/physics_body_3d.h10
-rw-r--r--scene/3d/ray_cast_3d.h2
-rw-r--r--scene/3d/remote_transform_3d.cpp2
-rw-r--r--scene/3d/skeleton_3d.cpp27
-rw-r--r--scene/3d/skeleton_3d.h11
-rw-r--r--scene/3d/skeleton_ik_3d.cpp2
-rw-r--r--scene/3d/skeleton_ik_3d.h2
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp6
-rw-r--r--scene/3d/spring_arm_3d.h2
-rw-r--r--scene/3d/sprite_3d.cpp62
-rw-r--r--scene/3d/sprite_3d.h21
-rw-r--r--scene/3d/vehicle_body_3d.cpp10
-rw-r--r--scene/3d/vehicle_body_3d.h2
-rw-r--r--scene/3d/visual_instance_3d.cpp6
-rw-r--r--scene/3d/visual_instance_3d.h1
-rw-r--r--scene/3d/voxel_gi.cpp4
-rw-r--r--scene/3d/voxelizer.cpp7
-rw-r--r--scene/3d/voxelizer.h2
-rw-r--r--scene/3d/world_environment.cpp8
-rw-r--r--scene/3d/xr_nodes.cpp14
-rw-r--r--scene/animation/animation_blend_tree.h2
-rw-r--r--scene/animation/animation_node_state_machine.cpp575
-rw-r--r--scene/animation/animation_node_state_machine.h39
-rw-r--r--scene/animation/animation_player.cpp388
-rw-r--r--scene/animation/animation_player.h75
-rw-r--r--scene/animation/animation_tree.cpp179
-rw-r--r--scene/animation/animation_tree.h18
-rw-r--r--scene/animation/root_motion_view.cpp9
-rw-r--r--scene/animation/tween.cpp149
-rw-r--r--scene/animation/tween.h13
-rw-r--r--scene/debugger/scene_debugger.cpp112
-rw-r--r--scene/debugger/scene_debugger.h8
-rw-r--r--scene/gui/aspect_ratio_container.cpp2
-rw-r--r--scene/gui/base_button.cpp12
-rw-r--r--scene/gui/base_button.h4
-rw-r--r--scene/gui/box_container.cpp2
-rw-r--r--scene/gui/button.cpp19
-rw-r--r--scene/gui/check_box.cpp4
-rw-r--r--scene/gui/check_button.cpp4
-rw-r--r--scene/gui/code_edit.cpp60
-rw-r--r--scene/gui/code_edit.h16
-rw-r--r--scene/gui/color_picker.cpp4
-rw-r--r--scene/gui/container.cpp2
-rw-r--r--scene/gui/control.cpp128
-rw-r--r--scene/gui/control.h16
-rw-r--r--scene/gui/dialogs.cpp15
-rw-r--r--scene/gui/dialogs.h14
-rw-r--r--scene/gui/file_dialog.cpp16
-rw-r--r--scene/gui/file_dialog.h52
-rw-r--r--scene/gui/flow_container.cpp6
-rw-r--r--scene/gui/gradient_edit.h4
-rw-r--r--scene/gui/graph_edit.cpp96
-rw-r--r--scene/gui/graph_edit.h59
-rw-r--r--scene/gui/graph_node.cpp84
-rw-r--r--scene/gui/graph_node.h12
-rw-r--r--scene/gui/grid_container.cpp27
-rw-r--r--scene/gui/item_list.cpp115
-rw-r--r--scene/gui/item_list.h2
-rw-r--r--scene/gui/label.cpp8
-rw-r--r--scene/gui/label.h6
-rw-r--r--scene/gui/line_edit.cpp62
-rw-r--r--scene/gui/line_edit.h10
-rw-r--r--scene/gui/link_button.cpp4
-rw-r--r--scene/gui/link_button.h6
-rw-r--r--scene/gui/menu_button.cpp6
-rw-r--r--scene/gui/menu_button.h4
-rw-r--r--scene/gui/option_button.cpp49
-rw-r--r--scene/gui/option_button.h8
-rw-r--r--scene/gui/popup.h2
-rw-r--r--scene/gui/popup_menu.cpp64
-rw-r--r--scene/gui/popup_menu.h13
-rw-r--r--scene/gui/progress_bar.cpp61
-rw-r--r--scene/gui/progress_bar.h16
-rw-r--r--scene/gui/range.cpp8
-rw-r--r--scene/gui/range.h4
-rw-r--r--scene/gui/rich_text_label.cpp436
-rw-r--r--scene/gui/rich_text_label.h41
-rw-r--r--scene/gui/scroll_container.cpp2
-rw-r--r--scene/gui/scroll_container.h4
-rw-r--r--scene/gui/spin_box.h4
-rw-r--r--scene/gui/subviewport_container.cpp6
-rw-r--r--scene/gui/tab_bar.cpp6
-rw-r--r--scene/gui/tab_container.cpp39
-rw-r--r--scene/gui/tab_container.h6
-rw-r--r--scene/gui/text_edit.cpp293
-rw-r--r--scene/gui/text_edit.h24
-rw-r--r--scene/gui/tree.cpp181
-rw-r--r--scene/gui/tree.h27
-rw-r--r--scene/gui/video_stream_player.cpp4
-rw-r--r--scene/gui/view_panner.cpp2
-rw-r--r--scene/main/canvas_item.cpp12
-rw-r--r--scene/main/canvas_item.h5
-rw-r--r--scene/main/canvas_layer.cpp2
-rw-r--r--scene/main/http_request.cpp23
-rw-r--r--scene/main/http_request.h11
-rw-r--r--scene/main/missing_node.cpp98
-rw-r--r--scene/main/missing_node.h63
-rw-r--r--scene/main/node.cpp297
-rw-r--r--scene/main/node.h52
-rw-r--r--scene/main/resource_preloader.cpp23
-rw-r--r--scene/main/resource_preloader.h6
-rw-r--r--scene/main/scene_tree.cpp194
-rw-r--r--scene/main/scene_tree.h32
-rw-r--r--scene/main/shader_globals_override.cpp26
-rw-r--r--scene/main/timer.cpp2
-rw-r--r--scene/main/viewport.cpp146
-rw-r--r--scene/main/viewport.h20
-rw-r--r--scene/main/window.cpp45
-rw-r--r--scene/main/window.h4
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp6
-rw-r--r--scene/multiplayer/multiplayer_spawner.h2
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp30
-rw-r--r--scene/multiplayer/scene_cache_interface.h8
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp10
-rw-r--r--scene/multiplayer/scene_replication_interface.h2
-rw-r--r--scene/multiplayer/scene_replication_state.cpp24
-rw-r--r--scene/multiplayer/scene_replication_state.h16
-rw-r--r--scene/register_scene_types.cpp40
-rw-r--r--scene/resources/animation.cpp17
-rw-r--r--scene/resources/animation.h8
-rw-r--r--scene/resources/animation_library.cpp151
-rw-r--r--scene/resources/animation_library.h66
-rw-r--r--scene/resources/audio_stream_sample.cpp6
-rw-r--r--scene/resources/bit_map.cpp4
-rw-r--r--scene/resources/canvas_item_material.cpp2
-rw-r--r--scene/resources/canvas_item_material.h17
-rw-r--r--scene/resources/concave_polygon_shape_3d.cpp4
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp2
-rw-r--r--scene/resources/curve.cpp161
-rw-r--r--scene/resources/curve.h4
-rw-r--r--scene/resources/default_theme/default_theme.cpp76
-rw-r--r--scene/resources/default_theme/default_theme.h2
-rw-r--r--scene/resources/default_theme/popup_menu_arrow_left.svg1
-rw-r--r--scene/resources/default_theme/popup_menu_arrow_right.svg1
-rw-r--r--scene/resources/environment.cpp7
-rw-r--r--scene/resources/font.cpp87
-rw-r--r--scene/resources/font.h6
-rw-r--r--scene/resources/gradient.cpp4
-rw-r--r--scene/resources/gradient.h2
-rw-r--r--scene/resources/immediate_mesh.cpp2
-rw-r--r--scene/resources/importer_mesh.cpp26
-rw-r--r--scene/resources/material.cpp138
-rw-r--r--scene/resources/material.h96
-rw-r--r--scene/resources/mesh.cpp28
-rw-r--r--scene/resources/mesh.h34
-rw-r--r--scene/resources/mesh_data_tool.cpp2
-rw-r--r--scene/resources/mesh_library.h4
-rw-r--r--scene/resources/multimesh.cpp12
-rw-r--r--scene/resources/navigation_mesh.cpp10
-rw-r--r--scene/resources/navigation_mesh.h8
-rw-r--r--scene/resources/packed_scene.cpp150
-rw-r--r--scene/resources/packed_scene.h10
-rw-r--r--scene/resources/particles_material.cpp30
-rw-r--r--scene/resources/particles_material.h42
-rw-r--r--scene/resources/polygon_path_finder.cpp36
-rw-r--r--scene/resources/polygon_path_finder.h4
-rw-r--r--scene/resources/primitive_meshes.cpp843
-rw-r--r--scene/resources/primitive_meshes.h142
-rw-r--r--scene/resources/resource_format_text.cpp503
-rw-r--r--scene/resources/resource_format_text.h68
-rw-r--r--scene/resources/shader.cpp17
-rw-r--r--scene/resources/shader.h16
-rw-r--r--scene/resources/skeleton_modification_2d.h2
-rw-r--r--scene/resources/skeleton_modification_2d_physicalbones.h2
-rw-r--r--scene/resources/skeleton_modification_3d.h2
-rw-r--r--scene/resources/skeleton_modification_stack_2d.cpp2
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp2
-rw-r--r--scene/resources/sky.cpp2
-rw-r--r--scene/resources/sky_material.cpp32
-rw-r--r--scene/resources/sky_material.h30
-rw-r--r--scene/resources/sphere_shape_3d.h2
-rw-r--r--scene/resources/sprite_frames.cpp46
-rw-r--r--scene/resources/sprite_frames.h14
-rw-r--r--scene/resources/style_box.cpp62
-rw-r--r--scene/resources/style_box.h4
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/syntax_highlighter.h4
-rw-r--r--scene/resources/text_file.cpp5
-rw-r--r--scene/resources/text_line.cpp34
-rw-r--r--scene/resources/text_paragraph.cpp32
-rw-r--r--scene/resources/texture.cpp504
-rw-r--r--scene/resources/texture.h174
-rw-r--r--scene/resources/theme.cpp355
-rw-r--r--scene/resources/theme.h3
-rw-r--r--scene/resources/tile_set.cpp38
-rw-r--r--scene/resources/tile_set.h52
-rw-r--r--scene/resources/visual_shader.cpp526
-rw-r--r--scene/resources/visual_shader.h28
-rw-r--r--scene/resources/visual_shader_nodes.cpp923
-rw-r--r--scene/resources/visual_shader_nodes.h124
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp16
-rw-r--r--scene/resources/visual_shader_particle_nodes.h4
-rw-r--r--scene/resources/world_2d.h4
-rw-r--r--scene/resources/world_3d.h4
274 files changed, 9692 insertions, 3984 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index 257e334873..4734f97e23 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -437,12 +437,23 @@ TypedArray<String> AnimatedSprite2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (frames.is_null()) {
- warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."));
+ warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames."));
}
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/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 7f7eae51a6..61a17a4845 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -86,7 +86,7 @@ TypedArray<String> CanvasModulate::get_configuration_warnings() const {
get_tree()->get_nodes_in_group("_canvas_modulate_" + itos(get_canvas().get_id()), &nodes);
if (nodes.size() > 1) {
- warnings.push_back(TTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
+ warnings.push_back(RTR("Only one visible CanvasModulate is allowed per scene (or set of instantiated scenes). The first created one will work, while the rest will be ignored."));
}
}
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index fbfe1d7eff..a8c12f4893 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -558,7 +558,7 @@ TypedArray<String> CollisionObject2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
- warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
+ warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
}
return warnings;
@@ -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 e3939130ec..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"
@@ -238,22 +239,25 @@ TypedArray<String> CollisionPolygon2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
}
int polygon_count = polygon.size();
if (polygon_count == 0) {
- warnings.push_back(TTR("An empty CollisionPolygon2D has no effect on collision."));
+ warnings.push_back(RTR("An empty CollisionPolygon2D has no effect on collision."));
} else {
bool solids = build_mode == BUILD_SOLIDS;
if (solids) {
if (polygon_count < 3) {
- warnings.push_back(TTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode."));
+ warnings.push_back(RTR("Invalid polygon. At least 3 points are needed in 'Solids' build mode."));
}
} else if (polygon_count < 2) {
- warnings.push_back(TTR("Invalid polygon. At least 2 points are needed in 'Segments' build mode."));
+ 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 db9a745436..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"
@@ -171,16 +172,19 @@ TypedArray<String> CollisionShape2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
- warnings.push_back(TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidDynamicBody2D, CharacterBody2D, etc. to give them a shape."));
}
if (!shape.is_valid()) {
- warnings.push_back(TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
+ 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;
if (convex.is_valid() || concave.is_valid()) {
- warnings.push_back(TTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."));
+ warnings.push_back(RTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."));
}
return warnings;
@@ -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 dd9df3c485..f5c14b6845 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -250,7 +250,7 @@ TypedArray<String> CPUParticles2D::get_configuration_warnings() const {
if (get_material().is_null() || (mat && !mat->get_particles_animation())) {
if (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 ||
get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid()) {
- warnings.push_back(TTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
+ warnings.push_back(RTR("CPUParticles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
}
}
@@ -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 8b0840e7c8..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,15 +287,24 @@ 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();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
+ warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
}
if (process_material.is_null()) {
- warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted."));
+ warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
} else {
CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
@@ -304,7 +313,7 @@ TypedArray<String> GPUParticles2D::get_configuration_warnings() const {
if (process &&
(process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
+ warnings.push_back(RTR("Particles2D animation requires the usage of a CanvasItemMaterial with \"Particles Animation\" enabled."));
}
}
}
@@ -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/joint_2d.cpp b/scene/2d/joint_2d.cpp
index c2773191ea..cd82b47333 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -77,15 +77,15 @@ void Joint2D::_update_joint(bool p_only_free) {
bool valid = false;
if (node_a && !body_a && node_b && !body_b) {
- warning = TTR("Node A and Node B must be PhysicsBody2Ds");
+ warning = RTR("Node A and Node B must be PhysicsBody2Ds");
} else if (node_a && !body_a) {
- warning = TTR("Node A must be a PhysicsBody2D");
+ warning = RTR("Node A must be a PhysicsBody2D");
} else if (node_b && !body_b) {
- warning = TTR("Node B must be a PhysicsBody2D");
+ warning = RTR("Node B must be a PhysicsBody2D");
} else if (!body_a || !body_b) {
- warning = TTR("Joint is not connected to two PhysicsBody2Ds");
+ warning = RTR("Joint is not connected to two PhysicsBody2Ds");
} else if (body_a == body_b) {
- warning = TTR("Node A and Node B must be different PhysicsBody2Ds");
+ warning = RTR("Node A and Node B must be different PhysicsBody2Ds");
} else {
warning = String();
valid = true;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index ba168eeb86..b09b1b5047 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -394,7 +394,7 @@ TypedArray<String> PointLight2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!texture.is_valid()) {
- warnings.push_back(TTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
+ warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
}
return warnings;
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 0310817592..14188d7120 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -250,11 +250,11 @@ TypedArray<String> LightOccluder2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!occluder_polygon.is_valid()) {
- warnings.push_back(TTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
+ warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
}
if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size() == 0) {
- warnings.push_back(TTR("The occluder polygon for this occluder is empty. Please draw a polygon."));
+ warnings.push_back(RTR("The occluder polygon for this occluder is empty. Please draw a polygon."));
}
return warnings;
@@ -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.cpp b/scene/2d/navigation_agent_2d.cpp
index 78b5a39e9a..91549d75f0 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -58,6 +58,9 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance);
+ ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent2D::set_navigable_layers);
+ ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent2D::get_navigable_layers);
+
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent2D::get_next_location);
@@ -79,6 +82,7 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_time_horizon", "get_time_horizon");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1"), "set_path_max_distance", "get_path_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigable_layers", "get_navigable_layers");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -127,8 +131,11 @@ NavigationAgent2D::~NavigationAgent2D() {
}
void NavigationAgent2D::set_navigable_layers(uint32_t p_layers) {
+ bool layers_changed = navigable_layers != p_layers;
navigable_layers = p_layers;
- update_navigation();
+ if (layers_changed) {
+ _request_repath();
+ }
}
uint32_t NavigationAgent2D::get_navigable_layers() const {
@@ -174,10 +181,7 @@ real_t NavigationAgent2D::get_path_max_distance() {
void NavigationAgent2D::set_target_location(Vector2 p_location) {
target_location = p_location;
- navigation_path.clear();
- target_reached = false;
- navigation_finished = false;
- update_frame_id = 0;
+ _request_repath();
}
Vector2 NavigationAgent2D::get_target_location() const {
@@ -244,7 +248,7 @@ TypedArray<String> NavigationAgent2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node2D>(get_parent())) {
- warnings.push_back(TTR("The NavigationAgent2D can be used only under a Node2D node."));
+ warnings.push_back(RTR("The NavigationAgent2D can be used only under a Node2D node."));
}
return warnings;
@@ -312,6 +316,13 @@ void NavigationAgent2D::update_navigation() {
}
}
+void NavigationAgent2D::_request_repath() {
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+ update_frame_id = 0;
+}
+
void NavigationAgent2D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index dcedc6506a..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();
@@ -139,6 +139,7 @@ public:
private:
void update_navigation();
+ void _request_repath();
void _check_distance_to_target();
};
diff --git a/scene/2d/navigation_obstacle_2d.cpp b/scene/2d/navigation_obstacle_2d.cpp
index 65f7adb7a6..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;
@@ -100,7 +103,12 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node2D>(get_parent())) {
- warnings.push_back(TTR("The NavigationObstacle2D only serves to provide collision avoidance to a Node2D object."));
+ 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 34ac02a82a..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 {
@@ -517,7 +521,7 @@ TypedArray<String> NavigationRegion2D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!navpoly.is_valid()) {
- warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon."));
+ warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon."));
}
}
@@ -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/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 849412a7ae..9e8ab224dc 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -143,7 +143,7 @@ TypedArray<String> ParallaxLayer::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
- warnings.push_back(TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
+ warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
}
return warnings;
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index d001652ca3..a21cb7db62 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -263,7 +263,7 @@ TypedArray<String> PathFollow2D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path2D>(get_parent())) {
- warnings.push_back(TTR("PathFollow2D only works when set as a child of a Path2D node."));
+ warnings.push_back(RTR("PathFollow2D only works when set as a child of a Path2D node."));
}
}
@@ -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.cpp b/scene/2d/physical_bone_2d.cpp
index 1fc4b651d8..2999736d64 100644
--- a/scene/2d/physical_bone_2d.cpp
+++ b/scene/2d/physical_bone_2d.cpp
@@ -110,15 +110,15 @@ TypedArray<String> PhysicalBone2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!parent_skeleton) {
- warnings.push_back(TTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
+ warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
}
if (parent_skeleton && bone2d_index <= -1) {
- warnings.push_back(TTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."));
+ warnings.push_back(RTR("A PhysicalBone2D needs to be assigned to a Bone2D node in order to function! Please set a Bone2D node in the inspector."));
}
if (!child_joint) {
PhysicalBone2D *parent_bone = Object::cast_to<PhysicalBone2D>(get_parent());
if (parent_bone) {
- warnings.push_back(TTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"));
+ warnings.push_back(RTR("A PhysicalBone2D node should have a Joint2D-based child node to keep bones connected! Please add a Joint2D-based node as a child to this node!"));
}
}
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 eb4d9d6445..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,8 +928,8 @@ 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) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ 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."));
}
return warnings;
@@ -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 {
@@ -1760,7 +1765,7 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
- ADD_GROUP("Moving platform", "moving_platform");
+ ADD_GROUP("Moving Platform", "moving_platform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
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/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index 429f0f6f6f..6c4bfd58ce 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -187,7 +187,7 @@ TypedArray<String> RemoteTransform2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
- warnings.push_back(TTR("Path property must point to a valid Node2D node to work."));
+ warnings.push_back(RTR("Path property must point to a valid Node2D node to work."));
}
return warnings;
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index a2c5d73b59..a2f4b16ed3 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -382,7 +382,7 @@ TypedArray<String> ShapeCast2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node2D::get_configuration_warnings();
if (shape.is_null()) {
- warnings.push_back(TTR("This node cannot interact with other objects unless a Shape2D is assigned."));
+ warnings.push_back(RTR("This node cannot interact with other objects unless a Shape2D is assigned."));
}
return warnings;
}
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index 15436d6e3d..78125b08bd 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -46,7 +46,7 @@ class ShapeCast2D : public Node2D {
RID shape_rid;
Vector2 target_position = Vector2(0, 50);
- Set<RID> exclude;
+ RBSet<RID> exclude;
real_t margin = 0.0;
uint32_t collision_mask = 1;
bool exclude_parent_body = true;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 360650c724..aa039e07ee 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -438,14 +438,14 @@ TypedArray<String> Bone2D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!skeleton) {
if (parent_bone) {
- warnings.push_back(TTR("This Bone2D chain should end at a Skeleton2D node."));
+ warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node."));
} else {
- warnings.push_back(TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."));
+ warnings.push_back(RTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node."));
}
}
if (rest == Transform2D(0, 0, 0, 0, 0, 0)) {
- warnings.push_back(TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."));
+ warnings.push_back(RTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one."));
}
return warnings;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index db33e6561a..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);
@@ -3586,7 +3588,7 @@ TypedArray<String> TileMap::get_configuration_warnings() const {
// Check if we have a non-sorted layer in a Z-index with a Y-sorted layer.
for (int layer = 0; layer < (int)layers.size(); layer++) {
if (!layers[layer].y_sort_enabled && y_sorted_z_index.has(layers[layer].z_index)) {
- warnings.push_back(TTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
+ warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers."));
break;
}
}
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/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 8623c7d8b6..d0aeffb166 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -107,17 +107,17 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const {
if (use_external_skeleton) {
if (external_skeleton_node_cache.is_null()) {
- warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node."));
+ warnings.push_back(RTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node."));
}
} else {
Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
if (!parent) {
- warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
+ warnings.push_back(RTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
}
}
if (bone_idx == -1) {
- warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node."));
+ warnings.push_back(RTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node."));
}
return warnings;
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 3ab09550fa..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");
@@ -689,7 +689,7 @@ TypedArray<String> CollisionObject3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
- warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
+ warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
}
return warnings;
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_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp
index 88ef44b71f..5a286d7b55 100644
--- a/scene/3d/collision_polygon_3d.cpp
+++ b/scene/3d/collision_polygon_3d.cpp
@@ -171,11 +171,11 @@ TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
}
if (polygon.is_empty()) {
- warnings.push_back(TTR("An empty CollisionPolygon3D has no effect on collision."));
+ warnings.push_back(RTR("An empty CollisionPolygon3D has no effect on collision."));
}
return warnings;
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index e1a0e1427b..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();
}
@@ -118,17 +118,17 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
- warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
+ warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidDynamicBody3D, CharacterBody3D, etc. to give them a shape."));
}
if (!shape.is_valid()) {
- warnings.push_back(TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."));
+ warnings.push_back(RTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."));
}
if (shape.is_valid() &&
Object::cast_to<RigidDynamicBody3D>(get_parent()) &&
Object::cast_to<ConcavePolygonShape3D>(*shape)) {
- warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static."));
+ warnings.push_back(RTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static."));
}
return warnings;
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index bd5595f974..5c32230942 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -37,14 +37,13 @@
class CollisionObject3D;
class CollisionShape3D : public Node3D {
GDCLASS(CollisionShape3D, Node3D);
- OBJ_CATEGORY("3D Physics Nodes");
Ref<Shape3D> shape;
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 8c8596fc2e..9666994b95 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -206,11 +206,11 @@ TypedArray<String> CPUParticles3D::get_configuration_warnings() const {
anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES);
if (!mesh_found) {
- warnings.push_back(TTR("Nothing is visible because no mesh has been assigned."));
+ warnings.push_back(RTR("Nothing is visible because no mesh has been assigned."));
}
if (!anim_material_found && (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
+ warnings.push_back(RTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
}
return warnings;
@@ -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/decal.cpp b/scene/3d/decal.cpp
index 8d0edfd1f3..a50f75f127 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -163,15 +163,15 @@ TypedArray<String> Decal::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) {
- warnings.push_back(TTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible."));
+ warnings.push_back(RTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible."));
}
if ((textures[TEXTURE_NORMAL].is_valid() || textures[TEXTURE_ORM].is_valid()) && textures[TEXTURE_ALBEDO].is_null()) {
- warnings.push_back(TTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0."));
+ warnings.push_back(RTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0."));
}
if (cull_mask == 0) {
- warnings.push_back(TTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property."));
+ warnings.push_back(RTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property."));
}
return warnings;
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 9fe2210de6..33ce9fc6fe 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -273,7 +273,7 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
+ warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
}
bool meshes_found = false;
@@ -300,17 +300,17 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
}
if (!meshes_found) {
- warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes."));
+ warnings.push_back(RTR("Nothing is visible because meshes have not been assigned to draw passes."));
}
if (process_material.is_null()) {
- warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted."));
+ warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
} else {
const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr());
if (!anim_material_found && process &&
(process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 ||
process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) {
- warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
+ warnings.push_back(RTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."));
}
}
@@ -352,15 +352,15 @@ TypedArray<String> GPUParticles3D::get_configuration_warnings() const {
}
if (dp_count && skin.is_valid()) {
- warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin."));
+ warnings.push_back(RTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin."));
} else if (dp_count == 0 && skin.is_null()) {
- warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found."));
+ warnings.push_back(RTR("Trails active, but neither Trail meshes or a Skin were found."));
} else if (dp_count > 1) {
- warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation)."));
+ warnings.push_back(RTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation)."));
}
if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) {
- warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering."));
+ warnings.push_back(RTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering."));
}
}
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/joint_3d.cpp b/scene/3d/joint_3d.cpp
index ce7c0d8292..c22e3f6d91 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -76,15 +76,15 @@ void Joint3D::_update_joint(bool p_only_free) {
PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b);
if (node_a && !body_a && node_b && !body_b) {
- warning = TTR("Node A and Node B must be PhysicsBody3Ds");
+ warning = RTR("Node A and Node B must be PhysicsBody3Ds");
} else if (node_a && !body_a) {
- warning = TTR("Node A must be a PhysicsBody3D");
+ warning = RTR("Node A must be a PhysicsBody3D");
} else if (node_b && !body_b) {
- warning = TTR("Node B must be a PhysicsBody3D");
+ warning = RTR("Node B must be a PhysicsBody3D");
} else if (!body_a && !body_b) {
- warning = TTR("Joint is not connected to any PhysicsBody3Ds");
+ warning = RTR("Joint is not connected to any PhysicsBody3Ds");
} else if (body_a == body_b) {
- warning = TTR("Node A and Node B must be different PhysicsBody3Ds");
+ warning = RTR("Node A and Node B must be different PhysicsBody3Ds");
} else {
warning = String();
}
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/light_3d.cpp b/scene/3d/light_3d.cpp
index 8396c23af7..c95806b2d0 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -497,7 +497,7 @@ TypedArray<String> OmniLight3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_shadow() && get_projector().is_valid()) {
- warnings.push_back(TTR("Projector texture only works with shadows active."));
+ warnings.push_back(RTR("Projector texture only works with shadows active."));
}
return warnings;
@@ -527,11 +527,11 @@ TypedArray<String> SpotLight3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) {
- warnings.push_back(TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."));
+ warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."));
}
if (!has_shadow() && get_projector().is_valid()) {
- warnings.push_back(TTR("Projector texture only works with shadows active."));
+ warnings.push_back(RTR("Projector texture only works with shadows active."));
}
return warnings;
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index 81c25f01c3..383fa644e5 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -35,7 +35,6 @@
class Light3D : public VisualInstance3D {
GDCLASS(Light3D, VisualInstance3D);
- OBJ_CATEGORY("3D Light Nodes");
public:
enum Param {
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index c74654b4bd..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 {
@@ -635,7 +634,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
bsud.to_percent = 0.8;
if (p_bake_step) {
- p_bake_step(0.0, TTR("Finding meshes, lights and probes"), p_bake_userdata, true);
+ p_bake_step(0.0, RTR("Finding meshes, lights and probes"), p_bake_userdata, true);
}
/* STEP 1, FIND MESHES, LIGHTS AND PROBES */
Vector<Lightmapper::MeshData> mesh_data;
@@ -655,7 +654,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
for (int m_i = 0; m_i < meshes_found.size(); m_i++) {
if (p_bake_step) {
float p = (float)(m_i) / meshes_found.size();
- p_bake_step(p * 0.1, vformat(TTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false);
+ p_bake_step(p * 0.1, vformat(RTR("Preparing geometry %d/%d"), m_i, meshes_found.size()), p_bake_userdata, false);
}
MeshesFound &mf = meshes_found.write[m_i];
@@ -790,7 +789,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
/* STEP 2, CREATE PROBES */
if (p_bake_step) {
- p_bake_step(0.3, TTR("Creating probes"), p_bake_userdata, true);
+ p_bake_step(0.3, RTR("Creating probes"), p_bake_userdata, true);
}
//bounds need to include the user probes
@@ -832,7 +831,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
for (int i = 0; i < mesh_data.size(); i++) {
if (p_bake_step) {
float p = (float)(i) / mesh_data.size();
- p_bake_step(0.3 + p * 0.1, vformat(TTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false);
+ p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false);
}
for (int j = 0; j < mesh_data[i].points.size(); j += 3) {
@@ -873,7 +872,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
// Add everything to lightmapper
if (p_bake_step) {
- p_bake_step(0.4, TTR("Preparing Lightmapper"), p_bake_userdata, true);
+ p_bake_step(0.4, RTR("Preparing Lightmapper"), p_bake_userdata, true);
}
{
@@ -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++) {
@@ -907,7 +906,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
// Add everything to lightmapper
if (environment_mode != ENVIRONMENT_MODE_DISABLED) {
if (p_bake_step) {
- p_bake_step(4.1, TTR("Preparing Environment"), p_bake_userdata, true);
+ p_bake_step(4.1, RTR("Preparing Environment"), p_bake_userdata, true);
}
environment_transform = get_global_transform().basis;
@@ -1046,7 +1045,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
//Obtain solved simplices
if (p_bake_step) {
- p_bake_step(0.8, TTR("Generating Probe Volumes"), p_bake_userdata, true);
+ p_bake_step(0.8, RTR("Generating Probe Volumes"), p_bake_userdata, true);
}
Vector<Delaunay3D::OutputSimplex> solved_simplices = Delaunay3D::tetrahedralize(points);
@@ -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
@@ -1130,7 +1128,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
}
if (p_bake_step) {
- p_bake_step(0.9, TTR("Generating Probe Acceleration Structures"), p_bake_userdata, true);
+ p_bake_step(0.9, RTR("Generating Probe Acceleration Structures"), p_bake_userdata, true);
}
_compute_bsp_tree(points, bsp_planes, planes_tested, bsp_simplices, bsp_simplex_indices, bsp_nodes);
@@ -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.cpp b/scene/3d/navigation_agent_3d.cpp
index c4f062f0f9..86c11b3789 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -62,6 +62,9 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent3D::set_path_max_distance);
ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent3D::get_path_max_distance);
+ ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent3D::set_navigable_layers);
+ ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent3D::get_navigable_layers);
+
ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location);
ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location);
ClassDB::bind_method(D_METHOD("get_next_location"), &NavigationAgent3D::get_next_location);
@@ -85,6 +88,7 @@ void NavigationAgent3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01"), "set_max_speed", "get_max_speed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1"), "set_path_max_distance", "get_path_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigable_layers", "get_navigable_layers");
ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
@@ -133,6 +137,18 @@ NavigationAgent3D::~NavigationAgent3D() {
agent = RID(); // Pointless
}
+void NavigationAgent3D::set_navigable_layers(uint32_t p_layers) {
+ bool layers_changed = navigable_layers != p_layers;
+ navigable_layers = p_layers;
+ if (layers_changed) {
+ _request_repath();
+ }
+}
+
+uint32_t NavigationAgent3D::get_navigable_layers() const {
+ return navigable_layers;
+}
+
void NavigationAgent3D::set_target_desired_distance(real_t p_dd) {
target_desired_distance = p_dd;
}
@@ -181,10 +197,7 @@ real_t NavigationAgent3D::get_path_max_distance() {
void NavigationAgent3D::set_target_location(Vector3 p_location) {
target_location = p_location;
- navigation_path.clear();
- target_reached = false;
- navigation_finished = false;
- update_frame_id = 0;
+ _request_repath();
}
Vector3 NavigationAgent3D::get_target_location() const {
@@ -250,7 +263,7 @@ TypedArray<String> NavigationAgent3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node3D>(get_parent())) {
- warnings.push_back(TTR("The NavigationAgent3D can be used only under a spatial node."));
+ warnings.push_back(RTR("The NavigationAgent3D can be used only under a spatial node."));
}
return warnings;
@@ -294,7 +307,7 @@ void NavigationAgent3D::update_navigation() {
}
if (reload_path) {
- navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true);
+ navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigable_layers);
navigation_finished = false;
nav_path_index = 0;
emit_signal(SNAME("path_changed"));
@@ -320,6 +333,13 @@ void NavigationAgent3D::update_navigation() {
}
}
+void NavigationAgent3D::_request_repath() {
+ navigation_path.clear();
+ target_reached = false;
+ navigation_finished = false;
+ update_frame_id = 0;
+}
+
void NavigationAgent3D::_check_distance_to_target() {
if (!target_reached) {
if (distance_to_target() < target_desired_distance) {
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index aebd5be7e4..283b99a24f 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -42,20 +42,22 @@ class NavigationAgent3D : public Node {
RID agent;
+ 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
@@ -63,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();
@@ -77,6 +79,9 @@ public:
return agent;
}
+ void set_navigable_layers(uint32_t p_layers);
+ uint32_t get_navigable_layers() const;
+
void set_target_desired_distance(real_t p_dd);
real_t get_target_desired_distance() const {
return target_desired_distance;
@@ -146,6 +151,7 @@ public:
private:
void update_navigation();
+ void _request_repath();
void _check_distance_to_target();
};
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 308545b2cc..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(TTR("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 8f0fd8706d..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) {
@@ -181,7 +194,7 @@ TypedArray<String> NavigationRegion3D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!navmesh.is_valid()) {
- warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work."));
+ warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work."));
}
}
@@ -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/node_3d.h b/scene/3d/node_3d.h
index 65d0e071cf..6d857a83ea 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -50,7 +50,6 @@ public:
class Node3D : public Node {
GDCLASS(Node3D, Node);
- OBJ_CATEGORY("3D");
public:
enum RotationEditMode {
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index b82f05544b..f848eaab2e 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -686,25 +686,25 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) {
- warnings.push_back(TTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
+ warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
}
if (bake_mask == 0) {
- warnings.push_back(TTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
+ warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
}
if (occluder.is_null()) {
- warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport."));
+ warnings.push_back(RTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport."));
} else {
Ref<ArrayOccluder3D> arr_occluder = occluder;
if (arr_occluder.is_valid() && arr_occluder->get_indices().size() < 3) {
// Setting a new ArrayOccluder3D from the inspector will create an empty occluder,
// so warn the user about this.
- warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
+ warnings.push_back(RTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
}
Ref<PolygonOccluder3D> poly_occluder = occluder;
if (poly_occluder.is_valid() && poly_occluder->get_polygon().size() < 3) {
- warnings.push_back(TTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport."));
+ warnings.push_back(RTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport."));
}
}
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 5fd28a6ff3..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;
@@ -253,11 +253,11 @@ TypedArray<String> PathFollow3D::get_configuration_warnings() const {
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path3D>(get_parent())) {
- warnings.push_back(TTR("PathFollow3D only works when set as a child of a Path3D node."));
+ warnings.push_back(RTR("PathFollow3D only works when set as a child of a Path3D node."));
} else {
Path3D *path = Object::cast_to<Path3D>(get_parent());
if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) {
- warnings.push_back(TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."));
+ warnings.push_back(RTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."));
}
}
}
@@ -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 47baa9e023..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,8 +991,8 @@ 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) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ 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."));
}
return warnings;
@@ -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;
}
@@ -2006,7 +2010,7 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
- ADD_GROUP("Moving platform", "moving_platform");
+ ADD_GROUP("Moving Platform", "moving_platform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
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/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index 8b714850d6..9979052385 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -182,7 +182,7 @@ TypedArray<String> RemoteTransform3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) {
- warnings.push_back(TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
+ warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
}
return warnings;
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 598897456d..f4a7767c44 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -32,7 +32,6 @@
#include "core/object/message_queue.h"
#include "core/variant/type_info.h"
-#include "editor/plugins/skeleton_3d_editor_plugin.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/surface_tool.h"
@@ -264,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();
@@ -510,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();
}
@@ -568,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();
}
@@ -586,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();
}
@@ -608,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();
}
@@ -617,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();
}
@@ -632,6 +636,7 @@ void Skeleton3D::remove_bone_child(int p_bone, int p_child) {
}
process_order_dirty = true;
+ rest_dirty = true;
_make_dirty();
}
@@ -644,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 {
@@ -652,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();
@@ -986,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());
}
@@ -1059,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;
@@ -1089,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
@@ -1207,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);
@@ -1284,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 eafb74f203..7d786a41bf 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -378,12 +378,12 @@ TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (mesh.is_null()) {
- warnings.push_back(TTR("This body will be ignored until you set a mesh."));
+ warnings.push_back(RTR("This body will be ignored until you set a mesh."));
}
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)) {
- warnings.push_back(TTR("Size changes to SoftDynamicBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ 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."));
}
return warnings;
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 514bd4aba0..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 {
@@ -1200,12 +1241,23 @@ StringName AnimatedSprite3D::get_animation() const {
TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const {
TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings();
if (frames.is_null()) {
- warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."));
+ warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."));
}
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 8d02d26fc4..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: {
@@ -109,7 +109,7 @@ TypedArray<String> VehicleWheel3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
- warnings.push_back(TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
+ warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
}
return warnings;
@@ -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/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index b8a68cdca9..669017c4b4 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -389,15 +389,15 @@ TypedArray<String> GeometryInstance3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) {
- warnings.push_back(TTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
+ warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
}
if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_begin) && Math::is_zero_approx(visibility_range_begin_margin)) {
- warnings.push_back(TTR("The GeometryInstance3D is configured to fade in smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range Begin Margin above 0."));
+ warnings.push_back(RTR("The GeometryInstance3D is configured to fade in smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range Begin Margin above 0."));
}
if ((visibility_range_fade_mode == VISIBILITY_RANGE_FADE_SELF || visibility_range_fade_mode == VISIBILITY_RANGE_FADE_DEPENDENCIES) && !Math::is_zero_approx(visibility_range_end) && Math::is_zero_approx(visibility_range_end_margin)) {
- warnings.push_back(TTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0."));
+ warnings.push_back(RTR("The GeometryInstance3D is configured to fade out smoothly over distance, but the fade transition distance is set to 0.\nTo resolve this, increase Visibility Range End Margin above 0."));
}
return warnings;
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 1c044ba681..f8fd4378fe 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -35,7 +35,6 @@
class VisualInstance3D : public Node3D {
GDCLASS(VisualInstance3D, Node3D);
- OBJ_CATEGORY("3D Visual Nodes");
RID base;
RID instance;
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index 6840d15f78..29e495ce1b 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -454,9 +454,9 @@ TypedArray<String> VoxelGI::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(TTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead."));
+ warnings.push_back(RTR("VoxelGIs are not supported by the OpenGL video driver.\nUse a LightmapGI instead."));
} else if (probe_data.is_null()) {
- warnings.push_back(TTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
+ warnings.push_back(RTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
}
return warnings;
}
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 300e761f39..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) {
@@ -139,7 +139,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!environment.is_valid() && !camera_effects.is_valid()) {
- warnings.push_back(TTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both."));
+ warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both."));
}
if (!is_inside_tree()) {
@@ -151,7 +151,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const {
}
if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) {
- warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes)."));
+ warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes)."));
}
return warnings;
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index efae81e048..1dad6078b4 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -95,7 +95,7 @@ TypedArray<String> XRCamera3D::get_configuration_warnings() const {
// must be child node of XROrigin3D!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- warnings.push_back(TTR("XRCamera3D must have an XROrigin3D node as its parent."));
+ warnings.push_back(RTR("XRCamera3D must have an XROrigin3D node as its parent."));
};
}
@@ -423,15 +423,15 @@ TypedArray<String> XRNode3D::get_configuration_warnings() const {
// must be child node of XROrigin!
XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent());
if (origin == nullptr) {
- warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent."));
+ warnings.push_back(RTR("XRController3D must have an XROrigin3D node as its parent."));
}
if (tracker_name == "") {
- warnings.push_back(TTR("No tracker name is set."));
+ warnings.push_back(RTR("No tracker name is set."));
}
if (pose_name == "") {
- warnings.push_back(TTR("No pose is set."));
+ warnings.push_back(RTR("No pose is set."));
}
}
@@ -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;
}
@@ -589,13 +589,13 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const {
if (is_visible() && is_inside_tree()) {
if (tracked_camera == nullptr) {
- warnings.push_back(TTR("XROrigin3D requires an XRCamera3D child node."));
+ warnings.push_back(RTR("XROrigin3D requires an XRCamera3D child node."));
}
}
bool xr_enabled = GLOBAL_GET("xr/shaders/enabled");
if (!xr_enabled) {
- warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
+ warnings.push_back(RTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
}
return warnings;
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 402418e5a9..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,18 +1298,190 @@ 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();
+
+ 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.
- clear_caches();
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 {
return animation_set.has(p_name);
}
@@ -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());
@@ -1789,12 +2036,16 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
bool AnimationPlayer::can_apply_reset() const {
return has_animation(SceneStringNames::get_singleton()->RESET) && playback.assigned != SceneStringNames::get_singleton()->RESET;
}
-#endif
+#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 c4fc69f370..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 {
@@ -62,7 +63,6 @@ public:
class AnimationPlayer : public Node {
GDCLASS(AnimationPlayer, Node);
- OBJ_CATEGORY("Animation Nodes");
public:
enum AnimationProcessCallback {
@@ -94,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
@@ -132,7 +132,7 @@ private:
Variant capture;
};
- Map<StringName, PropertyAnim> property_anim;
+ HashMap<StringName, PropertyAnim> property_anim;
struct BezierAnim {
Vector<StringName> bezier_property;
@@ -142,7 +142,7 @@ private:
uint64_t accum_pass = 0;
};
- Map<StringName, BezierAnim> bezier_anim;
+ HashMap<StringName, BezierAnim> bezier_anim;
uint32_t last_setup_pass = 0;
TrackNodeCache() {}
@@ -153,6 +153,16 @@ private:
int bone_idx = -1;
int blend_shape_idx = -1;
+ static uint32_t hash(const TrackNodeCacheKey &p_key) {
+ uint32_t h = hash_one_uint64(p_key.id);
+ h = hash_djb2_one_32(p_key.bone_idx, h);
+ return hash_djb2_one_32(p_key.blend_shape_idx, h);
+ }
+
+ inline bool operator==(const TrackNodeCacheKey &p_right) const {
+ return id == p_right.id && bone_idx == p_right.bone_idx && blend_shape_idx == p_right.blend_shape_idx;
+ }
+
inline bool operator<(const TrackNodeCacheKey &p_right) const {
if (id == p_right.id) {
if (blend_shape_idx == p_right.blend_shape_idx) {
@@ -166,7 +176,7 @@ private:
}
};
- Map<TrackNodeCacheKey, TrackNodeCache> node_cache_map;
+ HashMap<TrackNodeCacheKey, TrackNodeCache, TrackNodeCacheKey> node_cache_map;
TrackNodeCache *cache_update[NODE_CACHE_UPDATE_MAX];
int cache_update_size = 0;
@@ -174,7 +184,7 @@ private:
int cache_update_prop_size = 0;
TrackNodeCache::BezierAnim *cache_update_bezier[NODE_CACHE_UPDATE_MAX];
int cache_update_bezier_size = 0;
- Set<TrackNodeCache *> playing_caches;
+ RBSet<TrackNodeCache *> playing_caches;
uint64_t accum_pass = 1;
float speed_scale = 1.0;
@@ -185,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;
@@ -262,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;
@@ -273,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;
@@ -341,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 309c2b5245..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;
@@ -1641,18 +1698,18 @@ TypedArray<String> AnimationTree::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!root.is_valid()) {
- warnings.push_back(TTR("No root AnimationNode for the graph is set."));
+ warnings.push_back(RTR("No root AnimationNode for the graph is set."));
}
if (!has_node(animation_player)) {
- warnings.push_back(TTR("Path to an AnimationPlayer node containing animations is not set."));
+ warnings.push_back(RTR("Path to an AnimationPlayer node containing animations is not set."));
} else {
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));
if (!player) {
- warnings.push_back(TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
+ warnings.push_back(RTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node."));
} else if (!player->has_node(player->get_root())) {
- warnings.push_back(TTR("The AnimationPlayer root node is not a valid node."));
+ warnings.push_back(RTR("The AnimationPlayer root node is not a valid node."));
}
}
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 3fa0cec302..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;
@@ -1782,10 +1788,10 @@ void CodeEdit::request_code_completion(bool p_force) {
/* Don't re-query if all existing options are quoted types, eg path, signal. */
bool ignored = code_completion_active && !code_completion_options.is_empty();
if (ignored) {
- ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT;
- const ScriptCodeCompletionOption *previous_option = nullptr;
+ ScriptLanguage::CodeCompletionKind kind = ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT;
+ const ScriptLanguage::CodeCompletionOption *previous_option = nullptr;
for (int i = 0; i < code_completion_options.size(); i++) {
- const ScriptCodeCompletionOption &current_option = code_completion_options[i];
+ const ScriptLanguage::CodeCompletionOption &current_option = code_completion_options[i];
if (!previous_option) {
previous_option = &current_option;
kind = current_option.kind;
@@ -1795,7 +1801,7 @@ void CodeEdit::request_code_completion(bool p_force) {
break;
}
}
- ignored = ignored && (kind == ScriptCodeCompletionOption::KIND_FILE_PATH || kind == ScriptCodeCompletionOption::KIND_NODE_PATH || kind == ScriptCodeCompletionOption::KIND_SIGNAL);
+ ignored = ignored && (kind == ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH || kind == ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL);
}
if (ignored) {
@@ -1817,9 +1823,9 @@ 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) {
- ScriptCodeCompletionOption completion_option;
- completion_option.kind = (ScriptCodeCompletionOption::Kind)p_type;
+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;
completion_option.insert_text = p_insert_text;
completion_option.font_color = p_text_color;
@@ -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);
@@ -2261,7 +2267,7 @@ void CodeEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes");
- ADD_GROUP("Auto brace completion", "auto_brace_completion_");
+ ADD_GROUP("Auto Brace Completion", "auto_brace_completion_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_enabled"), "set_auto_brace_completion_enabled", "is_auto_brace_completion_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_highlight_matching"), "set_highlight_matching_braces_enabled", "is_highlight_matching_braces_enabled");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "auto_brace_completion_pairs"), "set_auto_brace_completion_pairs", "get_auto_brace_completion_pairs");
@@ -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) {
@@ -2702,7 +2708,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
TypedArray<Dictionary> completion_options_sources;
completion_options_sources.resize(code_completion_option_sources.size());
int i = 0;
- for (const ScriptCodeCompletionOption &E : code_completion_option_sources) {
+ for (const ScriptLanguage::CodeCompletionOption &E : code_completion_option_sources) {
Dictionary option;
option["kind"] = E.kind;
option["display_text"] = E.display;
@@ -2727,8 +2733,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
/* Convert back into options. */
int max_width = 0;
for (i = 0; i < completion_options.size(); i++) {
- ScriptCodeCompletionOption option;
- option.kind = (ScriptCodeCompletionOption::Kind)(int)completion_options[i].get("kind");
+ ScriptLanguage::CodeCompletionOption option;
+ option.kind = (ScriptLanguage::CodeCompletionKind)(int)completion_options[i].get("kind");
option.display = completion_options[i].get("display_text");
option.insert_text = completion_options[i].get("insert_text");
option.font_color = completion_options[i].get("font_color");
@@ -2821,15 +2827,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_options.clear();
code_completion_base = string_to_complete;
- Vector<ScriptCodeCompletionOption> completion_options_casei;
- Vector<ScriptCodeCompletionOption> completion_options_substr;
- Vector<ScriptCodeCompletionOption> completion_options_substr_casei;
- Vector<ScriptCodeCompletionOption> completion_options_subseq;
- Vector<ScriptCodeCompletionOption> completion_options_subseq_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr_casei;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq;
+ Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq_casei;
int max_width = 0;
String string_to_complete_lower = string_to_complete.to_lower();
- for (ScriptCodeCompletionOption &option : code_completion_option_sources) {
+ for (ScriptLanguage::CodeCompletionOption &option : code_completion_option_sources) {
if (single_quote && option.display.is_quoted()) {
option.display = option.display.unquote().quote("'");
}
@@ -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 cb1309ced3..0b00735f46 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -38,7 +38,7 @@ class CodeEdit : public TextEdit {
public:
/* Keep enum in sync with: */
- /* /core/object/script_language.h - ScriptCodeCompletionOption::Kind */
+ /* /core/object/script_language.h - ScriptLanguage::CodeCompletionKind */
enum CodeCompletionKind {
KIND_CLASS,
KIND_FUNCTION,
@@ -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;
@@ -208,15 +208,15 @@ private:
Color code_completion_existing_color = Color(0, 0, 0, 0);
bool code_completion_active = false;
- Vector<ScriptCodeCompletionOption> code_completion_options;
+ Vector<ScriptLanguage::CodeCompletionOption> code_completion_options;
int code_completion_line_ofs = 0;
int code_completion_current_selected = 0;
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
- Set<char32_t> code_completion_prefixes;
- List<ScriptCodeCompletionOption> code_completion_option_submitted;
- List<ScriptCodeCompletionOption> code_completion_option_sources;
+ RBSet<char32_t> code_completion_prefixes;
+ List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted;
+ List<ScriptLanguage::CodeCompletionOption> code_completion_option_sources;
String code_completion_base;
void _filter_code_completion_candidates_impl();
@@ -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 9f32ac223c..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) {
@@ -1221,7 +1221,7 @@ ColorPicker::ColorPicker() :
hhb->add_child(text_type);
text_type->set_text("#");
- text_type->set_tooltip(TTR("Switch between hexadecimal and code values."));
+ text_type->set_tooltip(RTR("Switch between hexadecimal and code values."));
if (Engine::get_singleton()->is_editor_hint()) {
text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled));
} else {
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 1dd88371ea..5512c0f1fd 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -196,7 +196,7 @@ TypedArray<String> Container::get_configuration_warnings() const {
TypedArray<String> warnings = Control::get_configuration_warnings();
if (get_class() == "Container" && get_script().is_null()) {
- warnings.push_back(TTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
+ warnings.push_back(RTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
}
return warnings;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index d2d1b5e9b7..54fa726260 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -367,7 +367,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const {
void Control::_get_property_list(List<PropertyInfo> *p_list) const {
Ref<Theme> theme = Theme::get_default();
- p_list->push_back(PropertyInfo(Variant::NIL, "Theme Overrides", PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::NIL, TTRC("Theme Overrides"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP));
{
List<StringName> names;
@@ -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();
@@ -2937,99 +2938,26 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}
-void Control::warp_mouse(const Point2 &p_to_pos) {
+void Control::warp_mouse(const Point2 &p_position) {
ERR_FAIL_COND(!is_inside_tree());
- get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos));
+ get_viewport()->warp_mouse(get_global_transform_with_canvas().xform(p_position));
}
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) {
@@ -3141,7 +3069,7 @@ TypedArray<String> Control::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
- warnings.push_back(TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
+ warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
}
return warnings;
@@ -3331,7 +3259,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_drag_preview", "control"), &Control::set_drag_preview);
ClassDB::bind_method(D_METHOD("is_drag_successful"), &Control::is_drag_successful);
- ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Control::warp_mouse);
+ ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Control::warp_mouse);
ClassDB::bind_method(D_METHOD("update_minimum_size"), &Control::update_minimum_size);
@@ -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 becb50a118..65b71d74f8 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -44,7 +44,6 @@ class Panel;
class Control : public CanvasItem {
GDCLASS(Control, CanvasItem);
- OBJ_CATEGORY("GUI Nodes");
public:
enum Anchor {
@@ -148,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 {
@@ -291,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;
@@ -548,7 +537,7 @@ public:
void grab_click_focus();
- void warp_mouse(const Point2 &p_to_pos);
+ void warp_mouse(const Point2 &p_position);
virtual bool is_text_field() const;
@@ -580,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.cpp b/scene/gui/dialogs.cpp
index be57ca9084..0bb96a18a5 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -39,13 +39,13 @@
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
+ if (close_on_escape && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_cancel_pressed();
}
}
void AcceptDialog::_parent_focused() {
- if (!is_exclusive()) {
+ if (close_on_escape && !is_exclusive()) {
_cancel_pressed();
}
}
@@ -145,6 +145,14 @@ bool AcceptDialog::get_hide_on_ok() const {
return hide_on_ok;
}
+void AcceptDialog::set_close_on_escape(bool p_hide) {
+ close_on_escape = p_hide;
+}
+
+bool AcceptDialog::get_close_on_escape() const {
+ return close_on_escape;
+}
+
void AcceptDialog::set_autowrap(bool p_autowrap) {
label->set_autowrap_mode(p_autowrap ? Label::AUTOWRAP_WORD : Label::AUTOWRAP_OFF);
}
@@ -288,6 +296,8 @@ void AcceptDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label);
ClassDB::bind_method(D_METHOD("set_hide_on_ok", "enabled"), &AcceptDialog::set_hide_on_ok);
ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok);
+ ClassDB::bind_method(D_METHOD("set_close_on_escape", "enabled"), &AcceptDialog::set_close_on_escape);
+ ClassDB::bind_method(D_METHOD("get_close_on_escape"), &AcceptDialog::get_close_on_escape);
ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button);
ClassDB::bind_method(D_METHOD("remove_button", "button"), &AcceptDialog::remove_button);
@@ -304,6 +314,7 @@ void AcceptDialog::_bind_methods() {
ADD_GROUP("Dialog", "dialog");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 1365b1df24..41fd9c0a10 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -45,11 +45,12 @@ 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;
void _custom_action(const String &p_action);
void _update_child_rects();
@@ -87,6 +88,9 @@ public:
void set_hide_on_ok(bool p_hide);
bool get_hide_on_ok() const;
+ void set_close_on_escape(bool p_enable);
+ bool get_close_on_escape() const;
+
void set_text(String p_text);
String get_text() const;
@@ -99,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 3c1f4bb93b..b2cf2f66a9 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();
}
@@ -211,11 +214,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 2fb6452a97..73f19a8eda 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -34,7 +34,7 @@ TypedArray<String> Range::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (shared->exp_ratio && shared->min <= 0) {
- warnings.push_back(TTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
+ warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
}
return warnings;
@@ -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;
@@ -282,7 +282,7 @@ void Range::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_greater"), "set_allow_greater", "is_greater_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed");
- GDVIRTUAL_BIND(_value_changed);
+ GDVIRTUAL_BIND(_value_changed, "new_value");
ADD_LINKED_PROPERTY("min_value", "value");
ADD_LINKED_PROPERTY("min_value", "max_value");
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 d585fb3a7a..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,7 +4637,10 @@ 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);
- // Note: set "bbcode_enabled" first, to avoid unnecessery "text" resets.
+ 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");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
@@ -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.cpp b/scene/gui/scroll_container.cpp
index b3cf2cbf7e..135bad4689 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -533,7 +533,7 @@ TypedArray<String> ScrollContainer::get_configuration_warnings() const {
}
if (found != 1) {
- warnings.push_back(TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."));
+ warnings.push_back(RTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually."));
}
return warnings;
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/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 799eea7eac..68281b6a72 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -176,7 +176,7 @@ void SubViewportContainer::input(const Ref<InputEvent> &p_event) {
return;
}
- Transform2D xform = get_global_transform();
+ Transform2D xform = get_global_transform_with_canvas();
if (stretch) {
Transform2D scale_xf;
@@ -203,7 +203,7 @@ void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
return;
}
- Transform2D xform = get_global_transform();
+ Transform2D xform = get_global_transform_with_canvas();
if (stretch) {
Transform2D scale_xf;
@@ -234,7 +234,7 @@ TypedArray<String> SubViewportContainer::get_configuration_warnings() const {
}
}
if (!has_viewport) {
- warnings.push_back(TTR("This node doesn't have a SubViewport as child, so it can't display its intended content.\nConsider adding a SubViewport as a child to provide something displayable."));
+ warnings.push_back(RTR("This node doesn't have a SubViewport as child, so it can't display its intended content.\nConsider adding a SubViewport as a child to provide something displayable."));
}
return warnings;
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 1697e743be..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("icon_separation"), 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++) {
@@ -497,6 +501,9 @@ void TabContainer::add_child_notify(Node *p_child) {
tab_bar->add_tab(p_child->get_name());
_update_margins();
+ if (get_tab_count() == 1) {
+ update();
+ }
p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
@@ -516,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++) {
@@ -545,10 +552,11 @@ void TabContainer::remove_child_notify(Node *p_child) {
tab_bar->remove_tab(get_tab_idx_from_control(c));
_update_margins();
-
- if (p_child->has_meta("_tab_name")) {
- p_child->remove_meta("_tab_name");
+ if (get_tab_count() == 0) {
+ update();
}
+
+ 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.
@@ -669,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);
}
@@ -727,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++) {
@@ -871,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);
@@ -890,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");
@@ -909,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 ff8d2b88b1..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;
}
@@ -1162,24 +1163,23 @@ Size2 TreeItem::get_minimum_size(int p_column) {
return cell.cached_minimum_size;
}
-Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
- return Variant();
+ return;
}
if (p_args[0]->get_type() != Variant::STRING && p_args[0]->get_type() != Variant::STRING_NAME) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
StringName method = *p_args[0];
call_recursive(method, &p_args[1], p_argcount - 1, r_error);
- return Variant();
}
void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@@ -1412,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"));
@@ -1754,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;
@@ -2089,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;
@@ -2097,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;
@@ -2671,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;
@@ -3207,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;
@@ -3995,6 +4012,7 @@ void Tree::clear() {
selected_item = nullptr;
edited_item = nullptr;
popup_edited_item = nullptr;
+ popup_pressing_edited_item = nullptr;
update();
};
@@ -4310,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);
@@ -4333,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;
@@ -4871,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 dc786de6dc..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);
@@ -189,7 +189,7 @@ protected:
return d;
}
- Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
public:
/* cell mode */
@@ -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/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index 71865b4864..892d0aba29 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -37,7 +37,7 @@
bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP));
+ Vector2 scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP));
if (scroll_vec != Vector2()) {
if (control_scheme == SCROLL_PANS) {
if (mb->is_ctrl_pressed()) {
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 700ba761f6..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) {
@@ -558,12 +555,12 @@ void HTTPRequest::set_https_proxy(const String &p_host, int p_port) {
client->set_https_proxy(p_host, p_port);
}
-void HTTPRequest::set_timeout(int p_timeout) {
+void HTTPRequest::set_timeout(double p_timeout) {
ERR_FAIL_COND(p_timeout < 0);
timeout = p_timeout;
}
-int HTTPRequest::get_timeout() {
+double HTTPRequest::get_timeout() {
return timeout;
}
@@ -615,7 +612,7 @@ void HTTPRequest::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000"), "set_body_size_limit", "get_body_size_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "timeout", PROPERTY_HINT_RANGE, "0,86400"), "set_timeout", "get_timeout");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeout", PROPERTY_HINT_RANGE, "0,3600,0.1,or_greater"), "set_timeout", "get_timeout");
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
@@ -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 62880fa282..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;
@@ -96,7 +96,7 @@ private:
int max_redirects = 8;
- int timeout = 0;
+ double timeout = 0;
void _redirect_request(const String &p_new_url);
@@ -144,10 +144,10 @@ public:
void set_max_redirects(int p_max);
int get_max_redirects() const;
- Timer *timer;
+ Timer *timer = nullptr;
- void set_timeout(int p_timeout);
- int get_timeout();
+ void set_timeout(double p_timeout);
+ double get_timeout();
void _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 208bbe4d72..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;
}
@@ -582,11 +595,11 @@ uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc
/***** RPC FUNCTIONS ********/
-Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
- return Variant();
+ return;
}
Variant::Type type = p_args[0]->get_type();
@@ -594,7 +607,7 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
StringName method = (*p_args[0]).operator StringName();
@@ -602,21 +615,20 @@ Variant Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallEr
rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
}
-Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 2;
- return Variant();
+ return;
}
if (p_args[0]->get_type() != Variant::INT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::INT;
- return Variant();
+ return;
}
Variant::Type type = p_args[1]->get_type();
@@ -624,7 +636,7 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return Variant();
+ return;
}
int peer_id = *p_args[0];
@@ -633,7 +645,6 @@ Variant Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::Cal
rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
- return Variant();
}
void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
@@ -642,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 {
@@ -837,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;
@@ -889,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()) {
@@ -1275,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;
@@ -1316,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();
@@ -1327,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]);
@@ -1350,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));
}
}
@@ -1361,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;
@@ -1470,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;
@@ -1498,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 {
@@ -1509,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;
@@ -1541,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;
@@ -1635,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 {
@@ -1884,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);
}
@@ -1921,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()) {
@@ -1967,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;
@@ -2159,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.
@@ -2179,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);
@@ -2190,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]);
@@ -2205,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);
@@ -2216,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]);
@@ -2365,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);
@@ -2373,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;
@@ -2395,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;
@@ -2413,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;
@@ -2487,11 +2608,11 @@ static void _Node_debug_sn(Object *p_obj) {
}
#endif // DEBUG_ENABLED
-void Node::_print_stray_nodes() {
- print_stray_nodes();
+void Node::_print_orphan_nodes() {
+ print_orphan_nodes();
}
-void Node::print_stray_nodes() {
+void Node::print_orphan_nodes() {
#ifdef DEBUG_ENABLED
ObjectDB::debug_objects(_Node_debug_sn);
#endif
@@ -2557,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 {
@@ -2617,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()) {
@@ -2624,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()) {
@@ -2635,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) {
}
@@ -2660,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);
@@ -2696,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);
@@ -2703,7 +2840,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Node::set_process_mode);
ClassDB::bind_method(D_METHOD("get_process_mode"), &Node::get_process_mode);
ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process);
- ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes);
+ ClassDB::bind_method(D_METHOD("print_orphan_nodes"), &Node::_print_orphan_nodes);
ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded);
ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded);
@@ -2737,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);
@@ -2747,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
@@ -2837,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");
@@ -2856,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 f5fbcf6587..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"
@@ -43,7 +43,6 @@ class PropertyTweener;
class Node : public Object {
GDCLASS(Node, Object);
- OBJ_CATEGORY("Nodes");
public:
enum ProcessMode {
@@ -100,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;
@@ -117,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;
@@ -137,6 +139,7 @@ private:
bool process_internal = false;
bool input = false;
+ bool shortcut_input = false;
bool unhandled_input = false;
bool unhandled_key_input = false;
@@ -169,18 +172,18 @@ private:
void _propagate_ready();
void _propagate_exit_tree();
void _propagate_after_exit_tree();
- void _print_stray_nodes();
+ void _print_orphan_nodes();
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
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;
- Variant _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- Variant _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
_FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; }
_FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
@@ -193,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--; }
@@ -216,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);
@@ -232,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>)
@@ -301,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);
@@ -342,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;
@@ -365,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;
@@ -396,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;
@@ -404,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
@@ -436,7 +452,7 @@ public:
void request_ready();
- static void print_stray_nodes();
+ static void print_orphan_nodes();
#ifdef TOOLS_ENABLED
String validate_child_name(Node *p_child);
@@ -499,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();
@@ -508,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 3ddce28b69..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;
@@ -912,34 +918,32 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
}
-Variant SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void SceneTree::_call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_COND_V(p_argcount < 3, Variant());
- ERR_FAIL_COND_V(!p_args[0]->is_num(), Variant());
- ERR_FAIL_COND_V(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING, Variant());
- ERR_FAIL_COND_V(p_args[2]->get_type() != Variant::STRING_NAME && p_args[2]->get_type() != Variant::STRING, Variant());
+ ERR_FAIL_COND(p_argcount < 3);
+ ERR_FAIL_COND(!p_args[0]->is_num());
+ ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(p_args[2]->get_type() != Variant::STRING_NAME && p_args[2]->get_type() != Variant::STRING);
int flags = *p_args[0];
StringName group = *p_args[1];
StringName method = *p_args[2];
call_group_flagsp(flags, group, method, p_args + 3, p_argcount - 3);
- return Variant();
}
-Variant SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+void SceneTree::_call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
- ERR_FAIL_COND_V(p_argcount < 2, Variant());
- ERR_FAIL_COND_V(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING, Variant());
- ERR_FAIL_COND_V(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING, Variant());
+ ERR_FAIL_COND(p_argcount < 2);
+ ERR_FAIL_COND(p_args[0]->get_type() != Variant::STRING_NAME && p_args[0]->get_type() != Variant::STRING);
+ ERR_FAIL_COND(p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING);
StringName group = *p_args[0];
StringName method = *p_args[1];
- call_group_flagsp(0, group, method, p_args + 2, p_argcount - 2);
- return Variant();
+ call_group_flagsp(GROUP_CALL_DEFAULT, group, method, p_args + 2, p_argcount - 2);
}
int64_t SceneTree::get_frame() const {
@@ -948,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];
}
@@ -974,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]);
}
@@ -1112,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;
}
@@ -1132,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) {
@@ -1144,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);
@@ -1213,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);
@@ -1224,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"));
@@ -1239,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);
}
@@ -1261,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 6197e52fc1..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;
@@ -175,8 +179,8 @@ private:
void make_group_changed(const StringName &p_group);
void _notify_group_pause(const StringName &p_group, int p_notification);
- Variant _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- Variant _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void _flush_delete_queue();
// Optimization.
@@ -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 5387dc01e2..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;
}
@@ -277,7 +275,7 @@ TypedArray<String> ShaderGlobalsOverride::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!active) {
- warnings.push_back(TTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
+ warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
}
return warnings;
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index 120b537e4f..5a5747e122 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -184,7 +184,7 @@ TypedArray<String> Timer::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (wait_time < 0.05 - CMP_EPSILON) {
- warnings.push_back(TTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
+ warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
}
return warnings;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 4525696071..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;
}
}
@@ -1038,8 +1038,8 @@ Transform2D Viewport::get_final_transform() const {
void Viewport::_update_canvas_items(Node *p_node) {
if (p_node != this) {
- Viewport *vp = Object::cast_to<Viewport>(p_node);
- if (vp) {
+ Window *w = Object::cast_to<Window>(p_node);
+ if (w && (!w->is_inside_tree() || !w->is_embedded())) {
return;
}
@@ -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);
}
@@ -1124,9 +1124,10 @@ Vector2 Viewport::get_mouse_position() const {
return gui.last_mouse_pos;
}
-void Viewport::warp_mouse(const Vector2 &p_pos) {
- Vector2 gpos = (get_final_transform().affine_inverse() * _get_input_pre_xform()).affine_inverse().xform(p_pos);
- Input::get_singleton()->warp_mouse_position(gpos);
+void Viewport::warp_mouse(const Vector2 &p_position) {
+ Transform2D xform = get_screen_transform();
+ Vector2 gpos = xform.xform(p_position).round();
+ Input::get_singleton()->warp_mouse(gpos);
}
void Viewport::_gui_sort_roots() {
@@ -1603,29 +1604,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(mouse_focus, mb);
}
- // In case the mouse was released after for example dragging a scrollbar,
- // check whether the current control is different from the stored one. If
- // it is different, rather than wait for it to be updated the next time the
- // mouse is moved, notify the control so that it can e.g. drop the highlight.
- // This code is duplicated from the mm.is_valid()-case further below.
- Control *over = nullptr;
- if (gui.mouse_focus) {
- over = gui.mouse_focus;
- } else {
- over = gui_find_control(mpos);
- }
-
- if (gui.mouse_focus_mask == MouseButton::NONE && over != gui.mouse_over) {
- _drop_mouse_over();
- _gui_cancel_tooltip();
-
- if (over) {
- _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
- }
- }
-
- gui.mouse_over = over;
-
set_input_as_handled();
}
}
@@ -1685,9 +1663,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
Control *over = nullptr;
- if (gui.mouse_focus) {
- over = gui.mouse_focus;
- } else if (gui.mouse_in_viewport) {
+ if (gui.mouse_in_viewport) {
over = gui_find_control(mpos);
}
@@ -1701,6 +1677,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
+ if (gui.mouse_focus) {
+ over = gui.mouse_focus;
+ }
+
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
if (over) {
@@ -1871,8 +1851,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (viewport_under) {
- Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform());
- viewport_pos = ai.xform(viewport_pos);
+ if (viewport_under != this) {
+ Transform2D ai = (viewport_under->get_final_transform().affine_inverse() * viewport_under->_get_input_pre_xform());
+ viewport_pos = ai.xform(viewport_pos);
+ }
// Find control under at position.
gui.drag_mouse_over = viewport_under->gui_find_control(viewport_pos);
if (gui.drag_mouse_over) {
@@ -2204,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);
@@ -2268,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()) {
@@ -2285,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()) {
@@ -2294,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()) {
@@ -2601,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;
@@ -2719,7 +2701,7 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
ev = p_event;
}
- if (is_embedding_subwindows() && _sub_windows_forward_input(p_event)) {
+ if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
set_input_as_handled();
return;
}
@@ -2762,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);
}
@@ -2833,7 +2822,7 @@ TypedArray<String> Viewport::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (size.x == 0 || size.y == 0) {
- warnings.push_back(TTR("Viewport size must be greater than 0 to render anything."));
+ warnings.push_back(RTR("Viewport size must be greater than 0 to render anything."));
}
return warnings;
}
@@ -3134,6 +3123,10 @@ Viewport::SDFScale Viewport::get_sdf_scale() const {
return sdf_scale;
}
+Transform2D Viewport::get_screen_transform() const {
+ return _get_input_pre_xform().affine_inverse() * get_final_transform();
+}
+
#ifndef _3D_DISABLED
AudioListener3D *Viewport::get_audio_listener_3d() const {
return audio_listener_3d;
@@ -3186,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;
}
@@ -3274,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;
}
@@ -3642,7 +3635,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
- ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse);
+ ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
@@ -3733,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
@@ -3751,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");
@@ -3761,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_");
@@ -3893,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.
@@ -3917,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);
@@ -3992,6 +3988,20 @@ Transform2D SubViewport::_stretch_transform() {
return transform;
}
+Transform2D SubViewport::get_screen_transform() const {
+ Transform2D container_transform = Transform2D();
+ SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
+ if (c) {
+ if (c->is_stretch_enabled()) {
+ container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
+ }
+ container_transform = c->get_viewport()->get_screen_transform() * c->get_global_transform_with_canvas() * container_transform;
+ } else {
+ WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
+ }
+ return container_transform * Viewport::get_screen_transform();
+}
+
void SubViewport::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index e4912f31c5..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;
@@ -547,7 +547,7 @@ public:
bool is_input_disabled() const;
Vector2 get_mouse_position() const;
- void warp_mouse(const Vector2 &p_pos);
+ void warp_mouse(const Vector2 &p_position);
void set_physics_object_picking(bool p_enable);
bool get_physics_object_picking();
@@ -609,11 +609,13 @@ public:
void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
+ virtual Transform2D get_screen_transform() const;
+
#ifndef _3D_DISABLED
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;
@@ -648,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);
@@ -732,6 +734,8 @@ public:
void set_clear_mode(ClearMode p_mode);
ClearMode get_clear_mode() const;
+ virtual Transform2D get_screen_transform() const override;
+
SubViewport();
~SubViewport();
};
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 6837fcae21..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);
}
@@ -1101,6 +1107,14 @@ void Window::popup_centered_ratio(float p_ratio) {
void Window::popup(const Rect2i &p_screen_rect) {
emit_signal(SNAME("about_to_popup"));
+ if (!_get_embedder() && get_flag(FLAG_POPUP)) {
+ // Send a focus-out notification when opening a Window Manager Popup.
+ SceneTree *scene_tree = get_tree();
+ if (scene_tree) {
+ scene_tree->notify_group("_viewports", NOTIFICATION_WM_WINDOW_FOCUS_OUT);
+ }
+ }
+
// Update window size to calculate the actual window size based on contents minimum size and minimum size.
_update_window_size();
@@ -1452,6 +1466,15 @@ void Window::_validate_property(PropertyInfo &property) const {
}
}
+Transform2D Window::get_screen_transform() const {
+ Transform2D embedder_transform = Transform2D();
+ if (_get_embedder()) {
+ embedder_transform.translate(get_position());
+ embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform;
+ }
+ return embedder_transform * Viewport::get_screen_transform();
+}
+
void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
diff --git a/scene/main/window.h b/scene/main/window.h
index 3d8e337b4a..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;
@@ -291,6 +291,8 @@ public:
Ref<Font> get_theme_default_font() const;
int get_theme_default_font_size() const;
+ virtual Transform2D get_screen_transform() const override;
+
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
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 52d6f6e63b..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();
@@ -809,6 +819,7 @@ void register_scene_types() {
GDREGISTER_VIRTUAL_CLASS(Texture2D);
GDREGISTER_CLASS(Sky);
GDREGISTER_CLASS(CompressedTexture2D);
+ GDREGISTER_CLASS(PortableCompressedTexture2D);
GDREGISTER_CLASS(ImageTexture);
GDREGISTER_CLASS(AtlasTexture);
GDREGISTER_CLASS(MeshTexture);
@@ -831,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);
@@ -895,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");
@@ -927,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");
@@ -954,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");
@@ -1077,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() {
@@ -1101,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);
@@ -1111,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()) {
@@ -1128,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 15594109e9..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,15 +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);
- if (f == nullptr) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + 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;
@@ -762,7 +768,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
f->get_buffer((unsigned char *)&magic, 4);
if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
// Binary BMFont file.
- ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3]));
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(RTR("Version %d of BMFont is not supported."), (int)magic[3]));
uint8_t block_type = f->get_8();
uint32_t block_size = f->get_32();
@@ -770,10 +776,10 @@ Error FontData::load_bitmap_font(const String &p_path) {
uint64_t off = f->get_position();
switch (block_type) {
case 1: /* info */ {
- ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size."));
+ ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size."));
base_size = f->get_16();
uint8_t flags = f->get_8();
- ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
if (flags & (1 << 3)) {
st_flags |= TextServer::FONT_BOLD;
}
@@ -794,7 +800,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
set_fixed_size(base_size);
} break;
case 2: /* common */ {
- ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size."));
+ ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, RTR("Invalid BMFont common block size."));
height = f->get_16();
ascent = f->get_16();
f->get_32(); // scale, skip
@@ -829,40 +835,40 @@ Error FontData::load_bitmap_font(const String &p_path) {
Ref<Image> img;
img.instantiate();
Error err = ImageLoader::load_image(file, img);
- ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file));
if (packed) {
if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_packed_8bit(img, page, base_size);
} else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_packed_4bit(img, page, base_size);
} else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
}
} else {
if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
set_texture_image(0, Vector2i(base_size, 0), page, img);
} else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_rgba_4bit(img, page, base_size);
} else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
_convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
} else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
} else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
} else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
}
}
}
@@ -899,7 +905,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
int texture_idx = f->get_8();
uint8_t channel = f->get_8();
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
int ch_off = 0;
switch (channel) {
case 1:
@@ -941,7 +947,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
}
} break;
default: {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type."));
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Invalid BMFont block type."));
} break;
}
f->seek(off + block_size);
@@ -958,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++;
@@ -1016,7 +1022,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
if (keys.has("face")) {
font_name = keys["face"];
}
- ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
} else if (type == "common") {
if (keys.has("lineHeight")) {
height = keys["lineHeight"].to_int();
@@ -1062,39 +1068,39 @@ Error FontData::load_bitmap_font(const String &p_path) {
Ref<Image> img;
img.instantiate();
Error err = ImageLoader::load_image(file, img);
- ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat(RTR("Can't load font texture: %s."), file));
if (packed) {
if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_packed_8bit(img, page, base_size);
} else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_packed_4bit(img, page, base_size);
} else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
}
} else {
if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
set_texture_image(0, Vector2i(base_size, 0), page, img);
} else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_rgba_4bit(img, page, base_size);
} else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
_convert_mono_8bit(img, page, first_ol_ch, base_size, 1);
} else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_4bit(img, page, first_cm_ch, base_size, 1);
} else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
outline = 0;
- ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
_convert_mono_8bit(img, page, first_gl_ch, base_size, 0);
} else {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
}
}
}
@@ -1144,7 +1150,7 @@ Error FontData::load_bitmap_font(const String &p_path) {
channel = keys["chnl"].to_int();
}
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
int ch_off = 0;
switch (channel) {
case 1:
@@ -1292,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 79ac1b57c3..a9c44dc6bf 100644
--- a/scene/resources/gradient.cpp
+++ b/scene/resources/gradient.cpp
@@ -71,7 +71,7 @@ void Gradient::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "interpolation_mode", PROPERTY_HINT_ENUM, "Linear,Constant,Cubic"), "set_interpolation_mode", "get_interpolation_mode");
- ADD_GROUP("Raw data", "");
+ ADD_GROUP("Raw Data", "");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "offsets"), "set_offsets", "get_offsets");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "colors"), "set_colors", "get_colors");
@@ -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 2f1ac7a83a..c7b1981aed 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -33,12 +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 "editor/editor_inspector.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
@@ -48,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 {
@@ -108,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]));
@@ -121,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;
@@ -131,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
@@ -185,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);
+ }
}
}
@@ -215,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);
@@ -244,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];
@@ -271,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
@@ -307,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();
+ }
}
}
@@ -322,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]) {
@@ -363,8 +402,11 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
- const Variant *args = binds.ptr();
- callable = callable.bind(&args, binds.size());
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * binds.size());
+ for (int j = 0; j < binds.size(); j++) {
+ argptrs[j] = &binds[j];
+ }
+ callable = callable.bind(argptrs, binds.size());
}
cfrom->connect(snames[c.signal], callable, varray(), CONNECT_PERSIST | c.flags);
@@ -388,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];
}
@@ -408,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
@@ -480,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 (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) && forced_value.get_type() == Variant::NIL) {
+ 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)) {
@@ -561,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
@@ -620,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;
}
@@ -827,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()) {
@@ -861,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 3113987fbc..5850d253e3 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -32,12 +32,12 @@
#include "core/core_string_names.h"
#include "core/io/image_loader.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
#include "core/os/os.h"
#include "mesh.h"
#include "scene/resources/bit_map.h"
#include "servers/camera/camera_feed.h"
-
int Texture2D::get_width() const {
int ret;
if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) {
@@ -338,9 +338,315 @@ ImageTexture::~ImageTexture() {
}
}
+/////////////////////
+
+void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
+ if (p_data.size() == 0) {
+ return; //nothing to do
+ }
+
+ const uint8_t *data = p_data.ptr();
+ uint32_t data_size = p_data.size();
+ ERR_FAIL_COND(data_size < 20);
+ compression_mode = CompressionMode(decode_uint32(data + 0));
+ format = Image::Format(decode_uint32(data + 4));
+ uint32_t mipmap_count = decode_uint32(data + 8);
+ size.width = decode_uint32(data + 12);
+ size.height = decode_uint32(data + 16);
+ mipmaps = mipmap_count > 1;
+
+ data += 20;
+ data_size -= 20;
+
+ Ref<Image> image;
+
+ switch (compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ Vector<uint8_t> image_data;
+
+ ERR_FAIL_COND(data_size < 4);
+ for (uint32_t i = 0; i < mipmap_count; i++) {
+ uint32_t mipsize = decode_uint32(data);
+ data += 4;
+ data_size -= 4;
+ ERR_FAIL_COND(mipsize < data_size);
+ Ref<Image> img = memnew(Image(data, data_size));
+ ERR_FAIL_COND(img->is_empty());
+ if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps.
+ img->convert(format);
+ }
+ image_data.append_array(img->get_data());
+
+ data += mipsize;
+ data_size -= mipsize;
+ }
+
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data)));
+
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ ERR_FAIL_COND(!Image::basis_universal_unpacker_ptr);
+ image = Image::basis_universal_unpacker_ptr(data, data_size);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20))));
+ } break;
+ }
+ ERR_FAIL_COND(image.is_null());
+
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ } else {
+ RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ }
+
+ image_stored = true;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+ alpha_cache.unref();
+
+ if (keep_all_compressed_buffers || keep_compressed_buffer) {
+ compressed_buffer = p_data;
+ } else {
+ compressed_buffer.clear();
+ }
+}
+
+PortableCompressedTexture2D::CompressionMode PortableCompressedTexture2D::get_compression_mode() const {
+ return compression_mode;
+}
+Vector<uint8_t> PortableCompressedTexture2D::_get_data() const {
+ return compressed_buffer;
+}
+
+void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map, float p_lossy_quality) {
+ ERR_FAIL_COND(p_image.is_null() || p_image->is_empty());
+
+ Vector<uint8_t> buffer;
+
+ buffer.resize(20);
+ encode_uint32(p_compression_mode, buffer.ptrw());
+ encode_uint32(p_image->get_format(), buffer.ptrw() + 4);
+ encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8);
+ encode_uint32(p_image->get_width(), buffer.ptrw() + 12);
+ encode_uint32(p_image->get_height(), buffer.ptrw() + 16);
+
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_LOSSLESS:
+ case COMPRESSION_MODE_LOSSY: {
+ for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
+ Vector<uint8_t> data;
+ if (p_compression_mode == COMPRESSION_MODE_LOSSY) {
+ data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
+ } else {
+ data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
+ }
+ int data_len = data.size();
+ buffer.resize(buffer.size() + 4);
+ encode_uint32(data_len, buffer.ptrw() + buffer.size() - 4);
+ buffer.append_array(data);
+ }
+ } break;
+ case COMPRESSION_MODE_BASIS_UNIVERSAL: {
+ Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC);
+ Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc);
+ buffer.append_array(budata);
+
+ } break;
+ case COMPRESSION_MODE_S3TC:
+ case COMPRESSION_MODE_ETC2:
+ case COMPRESSION_MODE_BPTC: {
+ Ref<Image> copy = p_image->duplicate();
+ switch (p_compression_mode) {
+ case COMPRESSION_MODE_S3TC:
+ copy->compress(Image::COMPRESS_S3TC);
+ break;
+ case COMPRESSION_MODE_ETC2:
+ copy->compress(Image::COMPRESS_ETC2);
+ break;
+ case COMPRESSION_MODE_BPTC:
+ copy->compress(Image::COMPRESS_BPTC);
+ break;
+ default: {
+ };
+ }
+
+ buffer.append_array(copy->get_data());
+
+ } break;
+ }
+
+ _set_data(buffer);
+}
+
+Image::Format PortableCompressedTexture2D::get_format() const {
+ return format;
+}
+
+Ref<Image> PortableCompressedTexture2D::get_image() const {
+ if (image_stored) {
+ return RenderingServer::get_singleton()->texture_2d_get(texture);
+ } else {
+ return Ref<Image>();
+ }
+}
+
+int PortableCompressedTexture2D::get_width() const {
+ return size.width;
+}
+
+int PortableCompressedTexture2D::get_height() const {
+ return size.height;
+}
+
+RID PortableCompressedTexture2D::get_rid() const {
+ if (texture.is_null()) {
+ //we are in trouble, create something temporary
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+bool PortableCompressedTexture2D::has_alpha() const {
+ return (format == Image::FORMAT_LA8 || format == Image::FORMAT_RGBA8);
+}
+
+void PortableCompressedTexture2D::draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, Rect2(p_pos, size), texture, false, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect(p_canvas_item, p_rect, texture, p_tile, p_modulate, p_transpose);
+}
+
+void PortableCompressedTexture2D::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, bool p_clip_uv) const {
+ if (size.width == 0 || size.height == 0) {
+ return;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, p_rect, texture, p_src_rect, p_modulate, p_transpose, p_clip_uv);
+}
+
+bool PortableCompressedTexture2D::is_pixel_opaque(int p_x, int p_y) const {
+ if (!alpha_cache.is_valid()) {
+ Ref<Image> img = get_image();
+ if (img.is_valid()) {
+ if (img->is_compressed()) { //must decompress, if compressed
+ Ref<Image> decom = img->duplicate();
+ decom->decompress();
+ img = decom;
+ }
+ alpha_cache.instantiate();
+ alpha_cache->create_from_image_alpha(img);
+ }
+ }
+
+ if (alpha_cache.is_valid()) {
+ int aw = int(alpha_cache->get_size().width);
+ int ah = int(alpha_cache->get_size().height);
+ if (aw == 0 || ah == 0) {
+ return true;
+ }
+
+ int x = p_x * aw / size.width;
+ int y = p_y * ah / size.height;
+
+ x = CLAMP(x, 0, aw);
+ y = CLAMP(y, 0, ah);
+
+ return alpha_cache->get_bit(Point2(x, y));
+ }
+
+ return true;
+}
+
+void PortableCompressedTexture2D::set_size_override(const Size2 &p_size) {
+ size_override = p_size;
+ RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height);
+}
+
+Size2 PortableCompressedTexture2D::get_size_override() const {
+ return size_override;
+}
+
+void PortableCompressedTexture2D::set_path(const String &p_path, bool p_take_over) {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->texture_set_path(texture, p_path);
+ }
+
+ Resource::set_path(p_path, p_take_over);
+}
+
+bool PortableCompressedTexture2D::keep_all_compressed_buffers = false;
+
+void PortableCompressedTexture2D::set_keep_all_compressed_buffers(bool p_keep) {
+ keep_all_compressed_buffers = p_keep;
+}
+
+bool PortableCompressedTexture2D::is_keeping_all_compressed_buffers() {
+ return keep_all_compressed_buffers;
+}
+
+void PortableCompressedTexture2D::set_keep_compressed_buffer(bool p_keep) {
+ keep_compressed_buffer = p_keep;
+ if (!p_keep) {
+ compressed_buffer.clear();
+ }
+}
+
+bool PortableCompressedTexture2D::is_keeping_compressed_buffer() const {
+ return keep_compressed_buffer;
+}
+
+void PortableCompressedTexture2D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_from_image", "image", "compression_mode", "normal_map", "lossy_quality"), &PortableCompressedTexture2D::create_from_image, DEFVAL(false), DEFVAL(0.8));
+ ClassDB::bind_method(D_METHOD("get_format"), &PortableCompressedTexture2D::get_format);
+ ClassDB::bind_method(D_METHOD("get_compression_mode"), &PortableCompressedTexture2D::get_compression_mode);
+
+ ClassDB::bind_method(D_METHOD("set_size_override", "size"), &PortableCompressedTexture2D::set_size_override);
+ ClassDB::bind_method(D_METHOD("get_size_override"), &PortableCompressedTexture2D::get_size_override);
+
+ ClassDB::bind_method(D_METHOD("set_keep_compressed_buffer", "keep"), &PortableCompressedTexture2D::set_keep_compressed_buffer);
+ ClassDB::bind_method(D_METHOD("is_keeping_compressed_buffer"), &PortableCompressedTexture2D::is_keeping_compressed_buffer);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &PortableCompressedTexture2D::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &PortableCompressedTexture2D::_get_data);
+
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("set_keep_all_compressed_buffers", "keep"), &PortableCompressedTexture2D::set_keep_all_compressed_buffers);
+ ClassDB::bind_static_method("PortableCompressedTexture2D", D_METHOD("is_keeping_all_compressed_buffers"), &PortableCompressedTexture2D::is_keeping_all_compressed_buffers);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_override"), "set_size_override", "get_size_override");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_compressed_buffer"), "set_keep_compressed_buffer", "is_keeping_compressed_buffer");
+
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSLESS);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_LOSSY);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BASIS_UNIVERSAL);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_S3TC);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_ETC2);
+ BIND_ENUM_CONSTANT(COMPRESSION_MODE_BPTC);
+}
+
+PortableCompressedTexture2D::PortableCompressedTexture2D() {}
+
+PortableCompressedTexture2D::~PortableCompressedTexture2D() {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
+
//////////////////////////////////////////
-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();
@@ -515,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();
@@ -561,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;
}
@@ -756,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);
@@ -764,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;
@@ -836,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 {
@@ -966,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);
@@ -1114,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);
@@ -1122,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;
@@ -1415,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());
}
@@ -1437,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());
}
@@ -1459,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());
}
@@ -1976,6 +2278,8 @@ Ref<Image> GradientTexture1D::get_image() const {
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
+//////////////////
+
GradientTexture2D::GradientTexture2D() {
_queue_update();
}
@@ -1997,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 {
@@ -2786,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);
@@ -2938,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;
@@ -2956,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;
@@ -3090,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 1e07b83547..317756e313 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -137,6 +137,78 @@ public:
~ImageTexture();
};
+class PortableCompressedTexture2D : public Texture2D {
+ GDCLASS(PortableCompressedTexture2D, Texture2D);
+
+public:
+ enum CompressionMode {
+ COMPRESSION_MODE_LOSSLESS,
+ COMPRESSION_MODE_LOSSY,
+ COMPRESSION_MODE_BASIS_UNIVERSAL,
+ COMPRESSION_MODE_S3TC,
+ COMPRESSION_MODE_ETC2,
+ COMPRESSION_MODE_BPTC,
+ };
+
+private:
+ CompressionMode compression_mode = COMPRESSION_MODE_LOSSLESS;
+ static bool keep_all_compressed_buffers;
+ bool keep_compressed_buffer = false;
+ Vector<uint8_t> compressed_buffer;
+ Size2 size;
+ Size2 size_override;
+ bool mipmaps = false;
+ Image::Format format = Image::FORMAT_L8;
+
+ mutable RID texture;
+ mutable Ref<BitMap> alpha_cache;
+
+ bool image_stored = false;
+
+protected:
+ Vector<uint8_t> _get_data() const;
+ void _set_data(const Vector<uint8_t> &p_data);
+
+ static void _bind_methods();
+
+public:
+ CompressionMode get_compression_mode() const;
+ void create_from_image(const Ref<Image> &p_image, CompressionMode p_compression_mode, bool p_normal_map = false, float p_lossy_quality = 0.8);
+
+ Image::Format get_format() const;
+
+ void update(const Ref<Image> &p_image);
+ Ref<Image> get_image() const override;
+
+ int get_width() const override;
+ int get_height() const override;
+
+ virtual RID get_rid() const override;
+
+ bool has_alpha() const override;
+ virtual void draw(RID p_canvas_item, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false) const override;
+ virtual void draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = true) const override;
+
+ bool is_pixel_opaque(int p_x, int p_y) const override;
+
+ virtual void set_path(const String &p_path, bool p_take_over = false) override;
+
+ void set_size_override(const Size2 &p_size);
+ Size2 get_size_override() const;
+
+ void set_keep_compressed_buffer(bool p_keep);
+ bool is_keeping_compressed_buffer() const;
+
+ static void set_keep_all_compressed_buffers(bool p_keep);
+ static bool is_keeping_all_compressed_buffers();
+
+ PortableCompressedTexture2D();
+ ~PortableCompressedTexture2D();
+};
+
+VARIANT_ENUM_CAST(PortableCompressedTexture2D::CompressionMode)
+
class CompressedTexture2D : public Texture2D {
GDCLASS(CompressedTexture2D, Texture2D);
@@ -181,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);
@@ -215,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;
@@ -480,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;
@@ -601,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;
@@ -939,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 1174117028..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 997b523d2e..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,19 +2665,17 @@ 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" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_view_matrix", "INV_VIEW_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "view_matrix", "VIEW_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection_matrix", "PROJECTION_MATRIX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection_matrix", "INV_PROJECTION_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "VIEWPORT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
@@ -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" },
@@ -2627,7 +2720,6 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "shadow_attenuation", "SHADOW_ATTENUATION" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
@@ -2648,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" },
@@ -2657,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" },
@@ -2676,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" },
@@ -2721,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" },
@@ -2737,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" },
@@ -2753,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" },
@@ -2771,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" },
@@ -2790,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" },
@@ -2809,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" },
@@ -2836,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)" },
@@ -2857,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" },
@@ -2868,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" },
@@ -2928,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 {
@@ -2939,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 "";
@@ -2971,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;
@@ -3106,7 +3196,7 @@ void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const {
}
if (port_list.is_empty()) {
- port_list = TTR("None");
+ port_list = RTR("None");
}
property.hint_string = port_list;
}
@@ -3187,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:
@@ -3211,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:
@@ -3240,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:
@@ -3312,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:
@@ -3381,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 },
@@ -3512,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++;
}
@@ -3545,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;
}
@@ -3649,7 +3742,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
List<String> keyword_list;
ShaderLanguage::get_keyword_list(&keyword_list);
if (keyword_list.find(uniform_name)) {
- return TTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
+ return RTR("Shader keywords cannot be used as uniform names.\nChoose another name.");
}
if (!is_qualifier_supported(qualifier)) {
String qualifier_str;
@@ -3665,11 +3758,11 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
default:
break;
}
- return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
+ return vformat(RTR("This uniform type does not support the '%s' qualifier."), qualifier_str);
} else if (qualifier == Qualifier::QUAL_GLOBAL) {
RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name);
if (gvt == RS::GLOBAL_VAR_TYPE_MAX) {
- return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
+ return vformat(RTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name);
}
bool incompatible_type = false;
switch (gvt) {
@@ -3698,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;
@@ -3727,7 +3825,7 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T
break;
}
if (incompatible_type) {
- return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
+ return vformat(RTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name);
}
}
@@ -4405,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;
@@ -4543,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:
@@ -4559,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
@@ -4719,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 c34aca32dc..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;
}
@@ -892,7 +905,7 @@ Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const {
String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
if (source == SOURCE_TEXTURE) {
@@ -917,12 +930,12 @@ String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::T
if (source == SOURCE_DEPTH && p_mode == Shader::MODE_SPATIAL && p_type == VisualShader::TYPE_FRAGMENT) {
if (get_output_port_for_preview() == 0) { // DEPTH_TEXTURE is not supported in preview(canvas_item) shader
- return TTR("Invalid source for preview.");
+ return RTR("Invalid source for preview.");
}
return String(); // all good
}
- return TTR("Invalid source for shader.");
+ return RTR("Invalid source for shader.");
}
void VisualShaderNodeTexture::_bind_methods() {
@@ -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;
}
@@ -1255,7 +1262,7 @@ void VisualShaderNodeSample3D::_bind_methods() {
String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
if (source == SOURCE_TEXTURE) {
@@ -1264,7 +1271,7 @@ String VisualShaderNodeSample3D::get_warning(Shader::Mode p_mode, VisualShader::
if (source == SOURCE_PORT) {
return String(); // all good
}
- return TTR("Invalid source for shader.");
+ return RTR("Invalid source for shader.");
}
VisualShaderNodeSample3D::VisualShaderNodeSample3D() {
@@ -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;
}
@@ -1575,7 +1573,7 @@ Vector<StringName> VisualShaderNodeCubemap::get_editable_properties() const {
String VisualShaderNodeCubemap::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (is_input_port_connected(2) && source != SOURCE_PORT) {
- return TTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
+ return RTR("The sampler port is connected but not used. Consider changing the source to 'SamplerPort'.");
}
return String();
}
@@ -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,14 +1966,14 @@ 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;
}
}
if (invalid_type) {
- return TTR("Invalid operator for that type.");
+ return RTR("Invalid operator for that type.");
}
return String();
@@ -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 TTR("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,12 +5782,12 @@ bool VisualShaderNodeTextureUniform::is_show_prop_names() const {
return true;
}
-Map<StringName, String> VisualShaderNodeTextureUniform::get_editable_properties_names() const {
- Map<StringName, String> names;
- names.insert("texture_type", TTR("Type"));
- names.insert("color_default", TTR("Default Color"));
- names.insert("texture_filter", TTR("Filter"));
- names.insert("texture_repeat", TTR("Repeat"));
+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"));
+ names.insert("texture_repeat", RTR("Repeat"));
return names;
}
@@ -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:
@@ -6458,7 +6607,7 @@ String VisualShaderNodeCompare::get_output_port_name(int p_port) const {
String VisualShaderNodeCompare::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const {
if (comparison_type == CTYPE_BOOLEAN || comparison_type == CTYPE_TRANSFORM) {
if (func > FUNC_NOT_EQUAL) {
- return TTR("Invalid comparison function for that type.");
+ return RTR("Invalid comparison function for that type.");
}
}
return "";
@@ -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 398c33c452..54df935168 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -74,9 +74,9 @@ Vector<StringName> VisualShaderNodeParticleEmitter::get_editable_properties() co
return props;
}
-Map<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
- Map<StringName, String> names;
- names.insert("mode_2d", TTR("2D Mode"));
+HashMap<StringName, String> VisualShaderNodeParticleEmitter::get_editable_properties_names() const {
+ HashMap<StringName, String> names;
+ names.insert("mode_2d", RTR("2D Mode"));
return names;
}
@@ -704,13 +704,13 @@ 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", TTR("Mesh"));
- names.insert("use_all_surfaces", TTR("Use All Surfaces"));
+ names.insert("mesh", RTR("Mesh"));
+ names.insert("use_all_surfaces", RTR("Use All Surfaces"));
if (!use_all_surfaces) {
- names.insert("surface_index", TTR("Surface Index"));
+ names.insert("surface_index", RTR("Surface Index"));
}
return names;
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();