summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp5
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp78
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/input_event_editor_plugin.cpp3
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp85
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp59
-rw-r--r--editor/plugins/node_3d_editor_plugin.h3
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp57
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp5
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp6
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp177
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp430
-rw-r--r--editor/plugins/tiles/tile_map_editor.h20
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp3
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp7
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp17
21 files changed, 545 insertions, 424 deletions
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index ae4482155c..2e4dcab203 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -83,7 +83,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
for (const StringName &E : classes) {
String name = String(E).replace_first("AnimationNode", "");
- if (name == "Animation") {
+ if (name == "Animation" || name == "StartState" || name == "EndState") {
continue;
}
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 4b7df75aec..86addde87b 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -107,7 +107,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
for (const StringName &E : classes) {
String name = String(E).replace_first("AnimationNode", "");
- if (name == "Animation") {
+ if (name == "Animation" || name == "StartState" || name == "EndState") {
continue; // nope
}
int idx = menu->get_item_count();
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 765d963846..98ccc1fdbe 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1573,7 +1573,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
frame = memnew(SpinBox);
hb->add_child(frame);
- frame->set_custom_minimum_size(Size2(60, 0));
+ frame->set_custom_minimum_size(Size2(80, 0) * EDSCALE);
frame->set_stretch_ratio(2);
frame->set_step(0.0001);
frame->set_tooltip(TTR("Animation position (in seconds)."));
@@ -1639,8 +1639,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
onion_skinning = memnew(MenuButton);
onion_skinning->set_tooltip(TTR("Onion Skinning Options"));
onion_skinning->get_popup()->add_separator(TTR("Directions"));
+ // TRANSLATORS: Opposite of "Future", refers to a direction in animation onion skinning.
onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST);
onion_skinning->get_popup()->set_item_checked(-1, true);
+ // TRANSLATORS: Opposite of "Past", refers to a direction in animation onion skinning.
onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE);
onion_skinning->get_popup()->add_separator(TTR("Depth"));
onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index bd9b89cbb7..2ba2466646 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -1220,7 +1220,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
for (const StringName &E : nodes) {
Ref<AnimationNode> anode = state_machine->get_node(E);
String name = E;
- bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr());
+ bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode);
Ref<StyleBox> sb = selected_nodes.has(E) ? style_selected : style;
Size2 s = sb->get_minimum_size();
@@ -1432,7 +1432,6 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
} else {
state_machine_draw->draw_texture(edit, nr.edit.position);
}
- offset.x += sep + edit->get_width();
}
}
@@ -1944,7 +1943,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
tool_erase_hb->add_child(tool_erase);
top_hb->add_child(memnew(VSeparator));
- top_hb->add_child(memnew(Label(TTR("Transition: "))));
+ top_hb->add_child(memnew(Label(TTR("Transition:"))));
transition_mode = memnew(OptionButton);
top_hb->add_child(transition_mode);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index d28629b41a..1ea0299d4e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1284,10 +1284,12 @@ void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) {
}
void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) {
- zoom_widget->set_zoom_by_increments(-1, p_alt);
- if (!Math::is_equal_approx(p_scroll_vec.y, (real_t)1.0)) {
+ int scroll_sign = (int)SIGN(p_scroll_vec.y);
+ // Inverted so that scrolling up (-1) zooms in, scrolling down (+1) zooms out.
+ zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt);
+ if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) {
// Handle high-precision (analog) scrolling.
- zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * p_scroll_vec.y + 1.f));
+ zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f));
}
_zoom_on_position(zoom_widget->get_zoom(), p_origin);
}
@@ -3777,7 +3779,7 @@ void CanvasItemEditor::_update_editor_settings() {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
- _update_context_menu_stylebox();
+ context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
pan_speed = int(EditorSettings::get_singleton()->get("editors/panning/2d_editor_pan_speed"));
@@ -3913,18 +3915,6 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
}
}
-void CanvasItemEditor::_update_context_menu_stylebox() {
- // This must be called when the theme changes to follow the new accent color.
- Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
- // Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
- context_menu_stylebox->set_border_color(accent_color);
- context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
- context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0);
- context_menu_container->add_theme_style_override("panel", context_menu_stylebox);
-}
-
void CanvasItemEditor::_update_scrollbars() {
updating_scroll = true;
@@ -4099,6 +4089,8 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
const HashMap<Node *, Object *> &selection = editor_selection->get_selection();
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ te->make_insert_queue();
for (const KeyValue<Node *, Object *> &E : selection) {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
@@ -4113,13 +4105,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos && p_location) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ te->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
}
if (key_rot && p_rotation) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ te->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
}
if (key_scale && p_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ te->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4145,13 +4137,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ te->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ te->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ te->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
}
}
}
@@ -4161,16 +4153,17 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ te->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
}
}
}
+ te->commit_insert_queue();
}
void CanvasItemEditor::_update_override_camera_button(bool p_game_running) {
@@ -4932,6 +4925,19 @@ CanvasItemEditor::CanvasItemEditor() {
controls_vb = memnew(VBoxContainer);
controls_vb->set_begin(Point2(5, 5));
+ // To ensure that scripts can parse the list of shortcuts correctly, we have to define
+ // those shortcuts one by one. Define shortcut before using it (by EditorZoomWidget)
+ ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5);
+ ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4);
+ ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3);
+ ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2);
+ ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1);
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), { (int32_t)Key::KEY_1, (int32_t)(KeyModifierMask::CMD | Key::KEY_0) });
+ ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2);
+ ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3);
+ ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4);
+ ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5);
+
zoom_widget = memnew(EditorZoomWidget);
controls_vb->add_child(zoom_widget);
zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
@@ -5156,11 +5162,12 @@ CanvasItemEditor::CanvasItemEditor() {
hb->add_child(memnew(VSeparator));
view_menu = memnew(MenuButton);
- view_menu->set_shortcut_context(this);
+ // TRANSLATORS: Noun, name of the 2D/3D View menus.
view_menu->set_text(TTR("View"));
+ view_menu->set_switch_on_hover(true);
+ view_menu->set_shortcut_context(this);
hb->add_child(view_menu);
view_menu->get_popup()->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
- view_menu->set_switch_on_hover(true);
p = view_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
@@ -5197,11 +5204,7 @@ CanvasItemEditor::CanvasItemEditor() {
context_menu_container = memnew(PanelContainer);
hbc_context_menu = memnew(HBoxContainer);
context_menu_container->add_child(hbc_context_menu);
- // Use a custom stylebox to make contextual menu items stand out from the rest.
- // This helps with editor usability as contextual menu items change when selecting nodes,
- // even though it may not be immediately obvious at first.
hb->add_child(context_menu_container);
- _update_context_menu_stylebox();
// Animation controls.
animation_hb = memnew(HBoxContainer);
@@ -5293,19 +5296,6 @@ CanvasItemEditor::CanvasItemEditor() {
// Store the singleton instance.
singleton = this;
- // To ensure that scripts can parse the list of shortcuts correctly, we have to define
- // those shortcuts one by one.
- ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5);
- ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1);
- ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), { (int32_t)Key::KEY_1, (int32_t)(KeyModifierMask::CMD | Key::KEY_0) });
- ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5);
-
set_process_shortcut_input(true);
// Update the menus' checkboxes
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index a4099079f3..c20a054800 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -492,8 +492,6 @@ private:
HSplitContainer *right_panel_split = nullptr;
VSplitContainer *bottom_split = nullptr;
- void _update_context_menu_stylebox();
-
void _set_owner_for_node_and_children(Node *p_node, Node *p_owner);
friend class CanvasItemEditorPlugin;
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index b022fcc1c9..fa971679e6 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -213,7 +213,7 @@ GPUParticles3DEditorBase::GPUParticles3DEditorBase() {
emission_fill->add_item(TTR("Surface Points"));
emission_fill->add_item(TTR("Surface Points+Normal (Directed)"));
emission_fill->add_item(TTR("Volume"));
- emd_vb->add_margin_child(TTR("Emission Source: "), emission_fill);
+ emd_vb->add_margin_child(TTR("Emission Source:"), emission_fill);
emission_dialog->get_ok_button()->set_text(TTR("Create"));
emission_dialog->connect("confirmed", callable_mp(this, &GPUParticles3DEditorBase::_generate_emission_points));
diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp
index fb0e260388..2acd0fca6e 100644
--- a/editor/plugins/input_event_editor_plugin.cpp
+++ b/editor/plugins/input_event_editor_plugin.cpp
@@ -84,8 +84,7 @@ InputEventConfigContainer::InputEventConfigContainer() {
input_event_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
add_child(input_event_text);
- open_config_button = memnew(Button);
- open_config_button->set_text(TTR("Configure"));
+ open_config_button = EditorInspector::create_inspector_action_button(TTR("Configure"));
open_config_button->connect("pressed", callable_mp(this, &InputEventConfigContainer::_configure_pressed));
add_child(open_config_button);
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 37922dd5c9..64aeb9f2a8 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -1497,6 +1497,9 @@ AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("Gizmo3DSamplePlayer"), SNAME("EditorIcons")));
create_material("stream_player_3d_material_primary", gizmo_color);
create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35));
+ // Enable vertex colors for the billboard material as the gizmo color depends on the
+ // AudioStreamPlayer3D attenuation type and source (Unit Size or Max Distance).
+ create_material("stream_player_3d_material_billboard", Color(1, 1, 1), true, false, true);
create_handle_material("handles");
}
@@ -1580,6 +1583,88 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
const Ref<Material> icon = get_material("stream_player_3d_icon", p_gizmo);
+ if (player->get_attenuation_model() != AudioStreamPlayer3D::ATTENUATION_DISABLED || player->get_max_distance() > CMP_EPSILON) {
+ // Draw a circle to represent sound volume attenuation.
+ // Use only a billboard circle to represent radius.
+ // This helps distinguish AudioStreamPlayer3D gizmos from OmniLight3D gizmos.
+ const Ref<Material> lines_billboard_material = get_material("stream_player_3d_material_billboard", p_gizmo);
+
+ // Soft distance cap varies depending on attenuation model, as some will fade out more aggressively than others.
+ // Multipliers were empirically determined through testing.
+ float soft_multiplier;
+ switch (player->get_attenuation_model()) {
+ case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
+ soft_multiplier = 12.0;
+ break;
+ case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
+ soft_multiplier = 4.0;
+ break;
+ case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
+ soft_multiplier = 3.25;
+ break;
+ default:
+ // Ensures Max Distance's radius visualization is not capped by Unit Size
+ // (when the attenuation mode is Disabled).
+ soft_multiplier = 10000.0;
+ break;
+ }
+
+ // Draw the distance at which the sound can be reasonably heard.
+ // This can be either a hard distance cap with the Max Distance property (if set above 0.0),
+ // or a soft distance cap with the Unit Size property (sound never reaches true zero).
+ // When Max Distance is 0.0, `r` represents the distance above which the
+ // sound can't be heard in *most* (but not all) scenarios.
+ float r;
+ if (player->get_max_distance() > CMP_EPSILON) {
+ r = MIN(player->get_unit_size() * soft_multiplier, player->get_max_distance());
+ } else {
+ r = player->get_unit_size() * soft_multiplier;
+ }
+ Vector<Vector3> points_billboard;
+
+ for (int i = 0; i < 120; i++) {
+ // Create a circle.
+ const float ra = Math::deg2rad((float)(i * 3));
+ const float rb = Math::deg2rad((float)((i + 1) * 3));
+ const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
+ const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
+
+ // Draw a billboarded circle.
+ points_billboard.push_back(Vector3(a.x, a.y, 0));
+ points_billboard.push_back(Vector3(b.x, b.y, 0));
+ }
+
+ Color color;
+ switch (player->get_attenuation_model()) {
+ // Pick cold colors for all attenuation models (except Disabled),
+ // so that soft caps can be easily distinguished from hard caps
+ // (which use warm colors).
+ case AudioStreamPlayer3D::ATTENUATION_INVERSE_DISTANCE:
+ color = Color(0.4, 0.8, 1);
+ break;
+ case AudioStreamPlayer3D::ATTENUATION_INVERSE_SQUARE_DISTANCE:
+ color = Color(0.4, 0.5, 1);
+ break;
+ case AudioStreamPlayer3D::ATTENUATION_LOGARITHMIC:
+ color = Color(0.4, 0.2, 1);
+ break;
+ default:
+ // Disabled attenuation mode.
+ // This is never reached when Max Distance is 0, but the
+ // hue-inverted form of this color will be used if Max Distance is greater than 0.
+ color = Color(1, 1, 1);
+ break;
+ }
+
+ if (player->get_max_distance() > CMP_EPSILON) {
+ // Sound is hard-capped by max distance. The attenuation model still matters,
+ // so invert the hue of the color that was chosen above.
+ color.set_h(color.get_h() + 0.5);
+ }
+
+ p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color);
+ }
+
if (player->is_emission_angle_enabled()) {
const float pc = player->get_emission_angle();
const float ofs = -Math::cos(Math::deg2rad(pc));
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index cbdb1e520a..9f4842a5a1 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -411,6 +411,12 @@ void Node3DEditorViewport::cancel_transform() {
set_message(TTR("Transform Aborted."), 3);
}
+void Node3DEditorViewport::_update_shrink() {
+ bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION));
+ subviewport_container->set_stretch_shrink(shrink ? 2 : 1);
+ subviewport_container->set_texture_filter(shrink ? TEXTURE_FILTER_NEAREST : TEXTURE_FILTER_PARENT_NODE);
+}
+
float Node3DEditorViewport::get_znear() const {
return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z);
}
@@ -2387,11 +2393,7 @@ void Node3DEditorViewport::_project_settings_changed() {
viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2));
viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3));
- bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION));
-
- if (shrink != (subviewport_container->get_stretch_shrink() > 1)) {
- subviewport_container->set_stretch_shrink(shrink ? 2 : 1);
- }
+ _update_shrink();
// Update MSAA, screen-space AA and debanding if changed
@@ -2399,6 +2401,9 @@ void Node3DEditorViewport::_project_settings_changed() {
viewport->set_msaa(Viewport::MSAA(msaa_mode));
const int ssaa_mode = GLOBAL_GET("rendering/anti_aliasing/quality/screen_space_aa");
viewport->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode));
+ const bool use_taa = GLOBAL_GET("rendering/anti_aliasing/quality/use_taa");
+ viewport->set_use_taa(use_taa);
+
const bool use_debanding = GLOBAL_GET("rendering/anti_aliasing/quality/use_debanding");
viewport->set_use_debanding(use_debanding);
@@ -3085,8 +3090,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_HALF_RESOLUTION: {
int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION);
bool current = view_menu->get_popup()->is_item_checked(idx);
- current = !current;
- view_menu->get_popup()->set_item_checked(idx, current);
+ view_menu->get_popup()->set_item_checked(idx, !current);
+ _update_shrink();
} break;
case VIEW_INFORMATION: {
int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION);
@@ -3124,7 +3129,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS:
case VIEW_DISPLAY_DEBUG_CLUSTER_DECALS:
case VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES:
- case VIEW_DISPLAY_DEBUG_OCCLUDERS: {
+ case VIEW_DISPLAY_DEBUG_OCCLUDERS:
+ case VIEW_DISPLAY_MOTION_VECTORS: {
static const int display_options[] = {
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
@@ -3152,6 +3158,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
VIEW_DISPLAY_DEBUG_CLUSTER_DECALS,
VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES,
VIEW_DISPLAY_DEBUG_OCCLUDERS,
+ VIEW_DISPLAY_MOTION_VECTORS,
VIEW_MAX
};
static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -3181,6 +3188,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
Viewport::DEBUG_DRAW_CLUSTER_DECALS,
Viewport::DEBUG_DRAW_CLUSTER_REFLECTION_PROBES,
Viewport::DEBUG_DRAW_OCCLUDERS,
+ Viewport::DEBUG_DRAW_MOTION_VECTORS,
};
int idx = 0;
@@ -4159,7 +4167,8 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) {
Vector3 motion_snapped = motion;
motion_snapped.snap(Vector3(snap, snap, snap));
// This might not be necessary anymore after issue #288 is solved (in 4.0?).
- set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ // TRANSLATORS: Refers to changing the scale of a node in the 3D editor.
+ set_message(TTR("Scaling:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
motion = _edit.original.basis.inverse().xform(motion);
@@ -4259,7 +4268,8 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) {
}
Vector3 motion_snapped = motion;
motion_snapped.snap(Vector3(snap, snap, snap));
- set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ // TRANSLATORS: Refers to changing the position of a node in the 3D editor.
+ set_message(TTR("Translating:") + " (" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion);
@@ -4545,6 +4555,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
display_submenu->add_radio_check_item(TTR("Decal Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_DECALS);
display_submenu->add_radio_check_item(TTR("ReflectionProbe Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES);
display_submenu->add_radio_check_item(TTR("Occlusion Culling Buffer"), VIEW_DISPLAY_DEBUG_OCCLUDERS);
+ display_submenu->add_radio_check_item(TTR("Motion Vectors"), VIEW_DISPLAY_MOTION_VECTORS);
display_submenu->set_name("display_advanced");
view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED);
@@ -6351,18 +6362,6 @@ void fragment() {
_generate_selection_boxes();
}
-void Node3DEditor::_update_context_menu_stylebox() {
- // This must be called when the theme changes to follow the new accent color.
- Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
- // Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
- context_menu_stylebox->set_border_color(accent_color);
- context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE));
- context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0);
- context_menu_container->add_theme_style_override("panel", context_menu_stylebox);
-}
-
void Node3DEditor::_update_gizmos_menu() {
gizmos_menu->clear();
@@ -6954,6 +6953,8 @@ void Node3DEditor::_update_theme() {
sun_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor"))));
+
+ context_menu_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles")));
}
void Node3DEditor::_notification(int p_what) {
@@ -6992,7 +6993,6 @@ void Node3DEditor::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_update_theme();
_update_gizmos_menu_theme();
- _update_context_menu_stylebox();
sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window")));
environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window")));
} break;
@@ -7702,6 +7702,7 @@ Node3DEditor::Node3DEditor() {
p->connect("id_pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed));
view_menu = memnew(MenuButton);
+ // TRANSLATORS: Noun, name of the 2D/3D View menus.
view_menu->set_text(TTR("View"));
view_menu->set_switch_on_hover(true);
view_menu->set_shortcut_context(this);
@@ -7712,11 +7713,7 @@ Node3DEditor::Node3DEditor() {
context_menu_container = memnew(PanelContainer);
hbc_context_menu = memnew(HBoxContainer);
context_menu_container->add_child(hbc_context_menu);
- // Use a custom stylebox to make contextual menu items stand out from the rest.
- // This helps with editor usability as contextual menu items change when selecting nodes,
- // even though it may not be immediately obvious at first.
hbc_menu->add_child(context_menu_container);
- _update_context_menu_stylebox();
// Get the view menu popup and have it stay open when a checkable item is selected
p = view_menu->get_popup();
@@ -8126,7 +8123,13 @@ void Node3DEditorPlugin::edit(Object *p_object) {
}
bool Node3DEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("Node3D");
+ if (p_object->is_class("Node3D")) {
+ return true;
+ } else {
+ // This ensures that gizmos are cleared when selecting a non-Node3D node.
+ const_cast<Node3DEditorPlugin *>(this)->edit((Object *)nullptr);
+ return false;
+ }
}
Dictionary Node3DEditorPlugin::get_state() const {
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 511135a5f1..8a602be08b 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -141,6 +141,7 @@ class Node3DEditorViewport : public Control {
VIEW_DISPLAY_DEBUG_CLUSTER_DECALS,
VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES,
VIEW_DISPLAY_DEBUG_OCCLUDERS,
+ VIEW_DISPLAY_MOTION_VECTORS,
VIEW_LOCK_ROTATION,
VIEW_CINEMATIC_PREVIEW,
@@ -251,6 +252,7 @@ private:
Transform3D _get_camera_transform() const;
int get_selected_count() const;
void cancel_transform();
+ void _update_shrink();
Vector3 _get_camera_position() const;
Vector3 _get_camera_normal() const;
@@ -674,7 +676,6 @@ private:
int camera_override_viewport_id;
void _init_indicators();
- void _update_context_menu_stylebox();
void _update_gizmos_menu();
void _update_gizmos_menu_theme();
void _init_grid();
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
index 79cf4c394e..d5fc51aea4 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
@@ -63,7 +63,7 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
break;
}
case OccluderInstance3D::BAKE_ERROR_CANT_SAVE: {
- EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path: ") + p_file);
+ EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path:") + " " + p_file);
break;
}
default: {
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 3851738cfa..36f559b2ae 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -37,6 +37,19 @@
#include "node_3d_editor_plugin.h"
#include "scene/resources/curve.h"
+static bool _is_in_handle(int p_id, int p_num_points) {
+ int t = (p_id + 1) % 2;
+ int idx = (p_id + 1) / 2;
+ // order of points is [out_0, out_1, in_1, out_2, in_2, ... out_n-1, in_n-1, in_n]
+ if (idx == 0) {
+ return false;
+ } else if (idx == (p_num_points - 1)) {
+ return true;
+ } else {
+ return (t == 1);
+ }
+}
+
String Path3DGizmo::get_handle_name(int p_id, bool p_secondary) const {
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
@@ -47,12 +60,10 @@ String Path3DGizmo::get_handle_name(int p_id, bool p_secondary) const {
return TTR("Curve Point #") + itos(p_id);
}
- p_id += 1; // Account for the first point only having an "out" handle
-
- int idx = p_id / 2;
- int t = p_id % 2;
+ // (p_id + 1) Accounts for the first point only having an "out" handle
+ int idx = (p_id + 1) / 2;
String n = TTR("Curve Point #") + itos(idx);
- if (t == 0) {
+ if (_is_in_handle(p_id, c->get_point_count())) {
n += " In";
} else {
n += " Out";
@@ -72,13 +83,11 @@ Variant Path3DGizmo::get_handle_value(int p_id, bool p_secondary) const {
return original;
}
- p_id += 1; // Account for the first point only having an "out" handle
-
- int idx = p_id / 2;
- int t = p_id % 2;
+ // (p_id + 1) Accounts for the first point only having an "out" handle
+ int idx = (p_id + 1) / 2;
Vector3 ofs;
- if (t == 0) {
+ if (_is_in_handle(p_id, c->get_point_count())) {
ofs = c->get_point_in(idx);
} else {
ofs = c->get_point_out(idx);
@@ -119,10 +128,8 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con
return;
}
- p_id += 1; // Account for the first point only having an "out" handle
-
- int idx = p_id / 2;
- int t = p_id % 2;
+ // (p_id + 1) Accounts for the first point only having an "out" handle
+ int idx = (p_id + 1) / 2;
Vector3 base = c->get_point_position(idx);
@@ -144,7 +151,7 @@ void Path3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, con
local.snap(Vector3(snap, snap, snap));
}
- if (t == 0) {
+ if (_is_in_handle(p_id, c->get_point_count())) {
c->set_point_in(idx, local);
if (Path3DEditorPlugin::singleton->mirror_angle_enabled()) {
c->set_point_out(idx, Path3DEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length));
@@ -179,12 +186,10 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res
return;
}
- p_id += 1; // Account for the first point only having an "out" handle
-
- int idx = p_id / 2;
- int t = p_id % 2;
+ // (p_id + 1) Accounts for the first point only having an "out" handle
+ int idx = (p_id + 1) / 2;
- if (t == 0) {
+ if (_is_in_handle(p_id, c->get_point_count())) {
if (p_cancel) {
c->set_point_in(p_id, p_restore);
return;
@@ -263,17 +268,17 @@ void Path3DGizmo::redraw() {
for (int i = 0; i < c->get_point_count(); i++) {
Vector3 p = c->get_point_position(i);
handles.push_back(p);
- if (i > 0) {
- v3p.push_back(p);
- v3p.push_back(p + c->get_point_in(i));
- sec_handles.push_back(p + c->get_point_in(i));
- }
-
+ // push Out points first so they get selected if the In and Out points are on top of each other.
if (i < c->get_point_count() - 1) {
v3p.push_back(p);
v3p.push_back(p + c->get_point_out(i));
sec_handles.push_back(p + c->get_point_out(i));
}
+ if (i > 0) {
+ v3p.push_back(p);
+ v3p.push_back(p + c->get_point_in(i));
+ sec_handles.push_back(p + c->get_point_in(i));
+ }
}
if (v3p.size() > 1) {
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index 16e874d7e2..79fc304242 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -420,11 +420,8 @@ ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin() {
preloader_editor = memnew(ResourcePreloaderEditor);
preloader_editor->set_custom_minimum_size(Size2(0, 250) * EDSCALE);
- button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("ResourcePreloader"), preloader_editor);
+ button = EditorNode::get_singleton()->add_bottom_panel_item("ResourcePreloader", preloader_editor);
button->hide();
-
- //preloader_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END);
- //preloader_editor->set_margin( MARGIN_TOP, 120 );
}
ResourcePreloaderEditorPlugin::~ResourcePreloaderEditorPlugin() {
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 6a63875324..ad817f9a41 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -546,7 +546,7 @@ Sprite2DEditor::Sprite2DEditor() {
debug_uv_dialog->connect("confirmed", callable_mp(this, &Sprite2DEditor::_create_node));
HBoxContainer *hb = memnew(HBoxContainer);
- hb->add_child(memnew(Label(TTR("Simplification: "))));
+ hb->add_child(memnew(Label(TTR("Simplification:"))));
simplification = memnew(SpinBox);
simplification->set_min(0.01);
simplification->set_max(10.00);
@@ -554,7 +554,7 @@ Sprite2DEditor::Sprite2DEditor() {
simplification->set_value(2);
hb->add_child(simplification);
hb->add_spacer();
- hb->add_child(memnew(Label(TTR("Shrink (Pixels): "))));
+ hb->add_child(memnew(Label(TTR("Shrink (Pixels):"))));
shrink_pixels = memnew(SpinBox);
shrink_pixels->set_min(0);
shrink_pixels->set_max(10);
@@ -562,7 +562,7 @@ Sprite2DEditor::Sprite2DEditor() {
shrink_pixels->set_value(0);
hb->add_child(shrink_pixels);
hb->add_spacer();
- hb->add_child(memnew(Label(TTR("Grow (Pixels): "))));
+ hb->add_child(memnew(Label(TTR("Grow (Pixels):"))));
grow_pixels = memnew(SpinBox);
grow_pixels->set_min(0);
grow_pixels->set_max(10);
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 244c718ebe..ec45341970 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -776,13 +776,13 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_advanced_menu = memnew(MenuButton);
button_advanced_menu->set_flat(true);
button_advanced_menu->set_toggle_mode(true);
- button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE);
- button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE);
+ button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE, Key::F);
+ button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE, Key::C);
button_advanced_menu->get_popup()->add_separator();
- button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT);
- button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT);
- button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY);
- button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT, Key::R);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT, Key::E);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY, Key::H);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY, Key::V);
button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed));
button_advanced_menu->set_focus_mode(FOCUS_ALL);
toolbar->add_child(button_advanced_menu);
@@ -1676,10 +1676,15 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Vector<Color> color;
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
+ p_canvas_item->draw_set_transform_matrix(p_transform * xform);
+ p_canvas_item->draw_polygon(polygon, color);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
p_canvas_item->draw_polygon(polygon, color);
@@ -1806,10 +1811,19 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ for (int j = 0; j < polygon.size(); j++) {
+ polygon.write[j] += position;
+ }
+ if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
+ // Draw terrain.
+ p_canvas_item->draw_polygon(polygon, color);
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
polygon.write[j] += position;
}
@@ -1850,10 +1864,16 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
Vector<Color> color;
color.push_back(Color(1.0, 1.0, 1.0, 0.5));
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
+ p_canvas_item->draw_set_transform_matrix(p_transform * xform);
+ p_canvas_item->draw_polygon(polygon, color);
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) {
p_canvas_item->draw_set_transform_matrix(p_transform * xform);
p_canvas_item->draw_polygon(polygon, color);
@@ -1926,10 +1946,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -1958,10 +1979,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -1970,12 +1992,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrains bits.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
+ if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2000,12 +2027,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ dummy_object->set("terrain", tile_data->get_terrain());
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
+ dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2044,10 +2076,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2085,10 +2118,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2097,12 +2131,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2138,10 +2176,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set());
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2154,10 +2193,11 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
@@ -2172,13 +2212,15 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
Dictionary dict = E.value;
Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
@@ -2224,20 +2266,30 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Vector2i coords = E.get_atlas_coords();
TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0);
+ Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ for (int j = 0; j < polygon.size(); j++) {
+ polygon.write[j] += position;
+ }
+ if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
+ // Draw terrain.
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.alternative_tile), tile_data->get_terrain());
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
-
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
polygon.write[j] += position;
}
if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) {
// Draw bit.
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), terrain);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_peering_bit_terrain(bit));
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2267,10 +2319,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2300,10 +2353,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
if (!drag_modified.has(cell)) {
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2312,12 +2366,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrains bits.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set());
+ if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
+
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit);
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit);
if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2343,12 +2403,18 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ dummy_object->set("terrain", tile_data->get_terrain());
+ }
+
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit));
+ dummy_object->set("terrain", tile_data->get_terrain_peering_bit(bit));
}
}
}
@@ -2380,7 +2446,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2405,10 +2471,11 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Save the old terrain_set and terrains bits.
Dictionary dict;
dict["terrain_set"] = tile_data->get_terrain_set();
+ dict["terrain"] = tile_data->get_terrain();
Array array;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1);
+ array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1);
}
dict["terrain_peering_bits"] = array;
drag_modified[cell] = dict;
@@ -2416,12 +2483,17 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrain bit.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+
+ Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set);
+ if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
+ tile_data->set_terrain(terrain);
+ }
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit);
if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) {
- tile_data->set_peering_bit_terrain(bit, terrain);
+ tile_data->set_terrain_peering_bit(bit, terrain);
}
}
}
@@ -2437,6 +2509,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Vector2i coords = E.key.get_atlas_coords();
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
@@ -2452,13 +2525,15 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
Dictionary dict = E.value;
Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), terrain);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain", coords.x, coords.y, E.key.alternative_tile), dict["terrain"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
- if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set"], bit)) {
undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 6c39244f80..d2f3eab31c 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -2028,7 +2028,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// --- Toolbar ---
toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
@@ -2321,7 +2320,7 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con
return tabs;
}
-HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
+HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return HashMap<Vector2i, TileMapCell>();
@@ -2332,105 +2331,87 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const
return HashMap<Vector2i, TileMapCell>();
}
- HashMap<Vector2i, TileMapCell> output;
-
- // Add the constraints from the added tiles.
- RBSet<TileMap::TerrainConstraint> added_tiles_constraints_set;
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- Vector2i coords = E_to_paint.key;
- TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
-
- RBSet<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
- for (const TileMap::TerrainConstraint &E : cell_constraints) {
- added_tiles_constraints_set.insert(E);
- }
- }
-
- // Build the list of potential tiles to replace.
- RBSet<Vector2i> potential_to_replace;
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- Vector2i coords = E_to_paint.key;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
- Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
- if (!p_to_paint.has(neighbor)) {
- potential_to_replace.insert(neighbor);
- }
- }
- }
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output;
+ if (p_connect) {
+ terrain_fill_output = tile_map->terrain_fill_connect(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
+ } else {
+ terrain_fill_output = tile_map->terrain_fill_path(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
}
- // Set of tiles to replace
- RBSet<Vector2i> to_replace;
-
- // Add the central tiles to the one to replace.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- to_replace.insert(E_to_paint.key);
+ // Make the painted path a set for faster lookups
+ HashSet<Vector2i> painted_set;
+ for (Vector2i coords : p_to_paint) {
+ painted_set.insert(coords);
}
- // Add the constraints from the surroundings of the modified areas.
- RBSet<TileMap::TerrainConstraint> removed_cells_constraints_set;
- bool to_replace_modified = true;
- while (to_replace_modified) {
- // Get the constraints from the removed cells.
- removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set, false);
-
- // Filter the sources to make sure they are in the potential_to_replace.
- RBMap<TileMap::TerrainConstraint, RBSet<Vector2i>> per_constraint_tiles;
- for (const TileMap::TerrainConstraint &E : removed_cells_constraints_set) {
- HashMap<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E.get_overlapping_coords_and_peering_bits();
- for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
- if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
- per_constraint_tiles[E].insert(E_source_tile_of_constraint.key);
- }
- }
- }
-
- to_replace_modified = false;
- for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
- TileMap::TerrainConstraint c = E;
- // Check if we have a conflict in constraints.
- if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
- // If we do, we search for a neighbor to remove.
- if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
- // Remove it.
- Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
- potential_to_replace.erase(to_add_to_remove);
- to_replace.insert(to_add_to_remove);
- to_replace_modified = true;
- for (KeyValue<TileMap::TerrainConstraint, RBSet<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
- E_source_tiles_of_constraint.value.erase(to_add_to_remove);
+ HashMap<Vector2i, TileMapCell> output;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
+ if (painted_set.has(E.key)) {
+ // Paint a random tile with the correct terrain for the painted path.
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ } else {
+ // Avoids updating the painted path from the output if the new pattern is the same as before.
+ bool keep_old = false;
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
+ if (cell.source_id != TileSet::INVALID_SOURCE) {
+ TileSetSource *source = *tile_set->get_source(cell.source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get tile data.
+ TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
+ if (tile_data && tile_data->get_terrains_pattern() == E.value) {
+ keep_old = true;
}
- break;
}
}
+ if (!keep_old) {
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ }
}
}
+ return output;
+}
- // Combine all constraints together.
- RBSet<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
- for (const TileMap::TerrainConstraint &E : added_tiles_constraints_set) {
- constraints.insert(E);
+HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return HashMap<Vector2i, TileMapCell>();
}
- // Remove the central tiles from the ones to replace.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- to_replace.erase(E_to_paint.key);
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return HashMap<Vector2i, TileMapCell>();
}
- // Run WFC to fill the holes with the constraints.
- HashMap<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
+ HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = tile_map->terrain_fill_pattern(tile_map_layer, p_to_paint, p_terrain_set, p_terrains_pattern, false);
- // Actually paint the tiles.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
- output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value);
+ // Make the painted path a set for faster lookups
+ HashSet<Vector2i> painted_set;
+ for (Vector2i coords : p_to_paint) {
+ painted_set.insert(coords);
}
- // Use the WFC run for the output.
- for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
- output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ HashMap<Vector2i, TileMapCell> output;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) {
+ if (painted_set.has(E.key)) {
+ // Paint a random tile with the correct terrain for the painted path.
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ } else {
+ // Avoids updating the painted path from the output if the new pattern is the same as before.
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key);
+ if (cell.source_id != TileSet::INVALID_SOURCE) {
+ TileSetSource *source = *tile_set->get_source(cell.source_id);
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ // Get tile data.
+ TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
+ if (tile_data && !(tile_data->get_terrains_pattern() == E.value)) {
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
+ }
+ }
+ }
+ }
}
-
return output;
}
@@ -2445,19 +2426,21 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
+ if (selected_type == SELECTED_TYPE_CONNECT) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
+ } else if (selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
- Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
- for (int i = 0; i < line.size(); i++) {
- to_draw[line[i]] = terrains_pattern;
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
+ return _draw_terrain_pattern(line, selected_terrain_set, terrains_pattern);
}
- return _draw_terrains(to_draw, selected_terrain_set);
}
HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
@@ -2471,25 +2454,29 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
-
Rect2i rect;
rect.set_position(p_start_cell);
rect.set_end(p_end_cell);
rect = rect.abs();
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
+ Vector<Vector2i> to_draw;
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
- to_draw[Vector2i(x, y)] = terrains_pattern;
+ to_draw.append(Vector2i(x, y));
}
}
- return _draw_terrains(to_draw, selected_terrain_set);
+
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+ return _draw_terrain_pattern(to_draw, selected_terrain_set, terrains_pattern);
+ }
}
RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
@@ -2614,20 +2601,23 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Ve
return HashMap<Vector2i, TileMapCell>();
}
- TileSet::TerrainsPattern terrains_pattern;
- if (p_erase) {
- terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- } else {
- terrains_pattern = selected_terrains_pattern;
- }
-
RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
- HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
- for (const Vector2i &coords : cells_to_draw) {
- to_draw[coords] = terrains_pattern;
+ Vector<Vector2i> cells_to_draw_as_vector;
+ for (Vector2i cell : cells_to_draw) {
+ cells_to_draw_as_vector.append(cell);
}
- return _draw_terrains(to_draw, selected_terrain_set);
+ if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
+ return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
+ } else { // SELECTED_TYPE_PATTERN
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
+ }
+ return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, terrains_pattern);
+ }
}
void TileMapEditorTerrainsPlugin::_stop_dragging() {
@@ -2696,11 +2686,13 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (tree_item) {
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
- in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
- if (in_meta_terrains_pattern == terrains_pattern) {
- terrains_tile_list->select(i);
- break;
+ if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
+ TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
+ in_meta_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
+ if (in_meta_terrains_pattern == terrains_pattern) {
+ terrains_tile_list->select(i);
+ break;
+ }
}
}
} else {
@@ -2773,22 +2765,33 @@ void TileMapEditorTerrainsPlugin::_update_selection() {
}
// Get the selected terrain.
- selected_terrains_pattern = TileSet::TerrainsPattern();
selected_terrain_set = -1;
+ selected_terrains_pattern = TileSet::TerrainsPattern();
TreeItem *selected_tree_item = terrains_tree->get_selected();
if (selected_tree_item && selected_tree_item->get_metadata(0)) {
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
// Selected terrain
selected_terrain_set = metadata_dict["terrain_set"];
+ selected_terrain = metadata_dict["terrain_id"];
- // Selected tile
+ // Selected mode/terrain pattern
if (erase_button->is_pressed()) {
+ selected_type = SELECTED_TYPE_PATTERN;
selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
} else if (terrains_tile_list->is_anything_selected()) {
metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
- selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
- selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ if (int(metadata_dict["type"]) == SELECTED_TYPE_CONNECT) {
+ selected_type = SELECTED_TYPE_CONNECT;
+ } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATH) {
+ selected_type = SELECTED_TYPE_PATH;
+ } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
+ selected_type = SELECTED_TYPE_PATTERN;
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ selected_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
+ } else {
+ ERR_FAIL();
+ }
}
}
}
@@ -2865,7 +2868,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
} else {
// Paint otherwise.
if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
@@ -2880,21 +2883,21 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
} else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
} else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_RECT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
- if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
return true;
}
drag_type = DRAG_TYPE_BUCKET;
@@ -3105,11 +3108,18 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
cell.alternative_tile = alternative_id;
TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
+
+ // Terrain center bit
+ int terrain = terrains_pattern.get_terrain();
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+ per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
+ }
+
// Terrain bits.
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- int terrain = terrains_pattern.get_terrain(bit);
+ if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
+ terrain = terrains_pattern.get_terrain_peering_bit(bit);
if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
}
@@ -3191,6 +3201,19 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
+ // Add the two first generic modes
+ int item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainConnect"), SNAME("EditorIcons")));
+ terrains_tile_list->set_item_tooltip(item_index, TTR("Connect mode: paints a terrain, then connects it with the surrounding tiles with the same terrain."));
+ Dictionary list_metadata_dict;
+ list_metadata_dict["type"] = SELECTED_TYPE_CONNECT;
+ terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
+
+ item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_theme_icon(SNAME("TerrainPath"), SNAME("EditorIcons")));
+ terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted withing the same stroke."));
+ list_metadata_dict = Dictionary();
+ list_metadata_dict["type"] = SELECTED_TYPE_PATH;
+ terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
+
// Sort the items in a map by the number of corresponding terrains.
RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted;
@@ -3200,7 +3223,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, bit) && E.get_terrain(bit) == selected_terrain_id) {
+ if (tile_set->is_valid_terrain_peering_bit(selected_terrain_set, bit) && E.get_terrain_peering_bit(bit) == selected_terrain_id) {
count++;
}
}
@@ -3241,12 +3264,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
}
// Create the ItemList's item.
- int item_index = terrains_tile_list->add_item("");
+ item_index = terrains_tile_list->add_item("");
terrains_tile_list->set_item_icon(item_index, icon);
terrains_tile_list->set_item_icon_region(item_index, region);
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
- Dictionary list_metadata_dict;
- list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array();
+ list_metadata_dict = Dictionary();
+ list_metadata_dict["type"] = SELECTED_TYPE_PATTERN;
+ list_metadata_dict["terrains_pattern"] = terrains_pattern.as_array();
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
}
}
@@ -3264,6 +3288,8 @@ void TileMapEditorTerrainsPlugin::_update_theme() {
picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
+
+ _update_tiles_list();
}
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
@@ -3303,7 +3329,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tile_list->set_max_columns(0);
terrains_tile_list->set_same_column_width(true);
- terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
+ terrains_tile_list->set_fixed_icon_size(Size2(32, 32) * EDSCALE);
terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
tilemap_tab_terrains->add_child(terrains_tile_list);
@@ -3396,7 +3422,7 @@ void TileMapEditor::_notification(int p_what) {
advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons")));
toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons")));
toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
- toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons")));
+ toggle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons")));
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
@@ -3428,66 +3454,16 @@ void TileMapEditor::_notification(int p_what) {
void TileMapEditor::_on_grid_toggled(bool p_pressed) {
EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed);
+ CanvasItemEditor::get_singleton()->update_viewport();
}
-void TileMapEditor::_layers_selection_button_draw() {
- if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
+void TileMapEditor::_layers_selection_item_selected(int p_index) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map || tile_map->get_layers_count() <= 0) {
return;
}
- RID ci = layers_selection_button->get_canvas_item();
- Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
-
- Color clr = Color(1, 1, 1);
- if (get_theme_constant(SNAME("modulate_arrow"))) {
- switch (layers_selection_button->get_draw_mode()) {
- case BaseButton::DRAW_PRESSED:
- clr = get_theme_color(SNAME("font_pressed_color"));
- break;
- case BaseButton::DRAW_HOVER:
- clr = get_theme_color(SNAME("font_hover_color"));
- break;
- case BaseButton::DRAW_DISABLED:
- clr = get_theme_color(SNAME("font_disabled_color"));
- break;
- default:
- if (layers_selection_button->has_focus()) {
- clr = get_theme_color(SNAME("font_focus_color"));
- } else {
- clr = get_theme_color(SNAME("font_color"));
- }
- }
- }
-
- Size2 size = layers_selection_button->get_size();
-
- Point2 ofs;
- if (is_layout_rtl()) {
- ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
- } else {
- ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2)));
- }
- Rect2 dst_rect = Rect2(ofs, arrow->get_size());
- if (!layers_selection_button->is_pressed()) {
- dst_rect.size = -dst_rect.size;
- }
- arrow->draw_rect(ci, dst_rect, false, clr);
-}
-
-void TileMapEditor::_layers_selection_button_pressed() {
- if (!layers_selection_popup->is_visible()) {
- Size2 size = layers_selection_popup->get_contents_minimum_size();
- size.x = MAX(size.x, layers_selection_button->get_size().x);
- layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y));
- layers_selection_popup->set_size(size);
- layers_selection_popup->popup();
- } else {
- layers_selection_popup->hide();
- }
-}
-
-void TileMapEditor::_layers_selection_id_pressed(int p_id) {
- tile_map_layer = p_id;
+ tile_map_layer = p_index;
_update_layers_selection();
}
@@ -3670,8 +3646,6 @@ void TileMapEditor::_layers_select_next_or_previous(bool p_next) {
}
void TileMapEditor::_update_layers_selection() {
- layers_selection_popup->clear();
-
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
@@ -3698,32 +3672,23 @@ void TileMapEditor::_update_layers_selection() {
} else {
tile_map_layer = -1;
}
- tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
+ tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
- // Build the list of layers.
- for (int i = 0; i < tile_map->get_layers_count(); i++) {
- String name = tile_map->get_layer_name(i);
- layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i);
- layers_selection_popup->set_item_as_radio_checkable(i, true);
- layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i));
- layers_selection_popup->set_item_checked(i, i == tile_map_layer);
- }
+ layers_selection_button->clear();
+ if (tile_map->get_layers_count() > 0) {
+ // Build the list of layers.
+ for (int i = 0; i < tile_map->get_layers_count(); i++) {
+ String name = tile_map->get_layer_name(i);
+ layers_selection_button->add_item(name.is_empty() ? vformat(TTR("Layer %d"), i) : name, i);
+ layers_selection_button->set_item_disabled(i, !tile_map->is_layer_enabled(i));
+ }
- // Update the button label.
- if (tile_map_layer >= 0) {
- layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer));
+ layers_selection_button->set_disabled(false);
+ layers_selection_button->select(tile_map_layer);
} else {
- layers_selection_button->set_text(TTR("Select a layer"));
- }
-
- // Set button minimum width.
- Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0);
- if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) {
- Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton"));
- min_button_size.x += arrow->get_size().x;
+ layers_selection_button->set_disabled(true);
+ layers_selection_button->set_text(TTR("No Layers"));
}
- layers_selection_button->set_custom_minimum_size(min_button_size);
- layers_selection_button->update();
tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
}
@@ -4000,7 +3965,6 @@ TileMapEditor::TileMapEditor() {
// TabBar.
tabs_bar = memnew(TabBar);
- tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
tabs_bar->set_clip_tabs(false);
for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
@@ -4029,31 +3993,23 @@ TileMapEditor::TileMapEditor() {
}
// Wide empty separation control.
- Control *h_empty_space = memnew(Control);
- h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_map_toolbar->add_child(h_empty_space);
+ tile_map_toolbar->add_spacer();
// Layer selector.
- layers_selection_popup = memnew(PopupMenu);
- layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed));
- layers_selection_popup->set_flag(Window::FLAG_POPUP, false);
-
- layers_selection_button = memnew(Button);
- layers_selection_button->set_toggle_mode(true);
- layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw));
- layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed));
- layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide));
- layers_selection_button->set_tooltip(TTR("Tile Map Layer"));
- layers_selection_button->add_child(layers_selection_popup);
+ layers_selection_button = memnew(OptionButton);
+ layers_selection_button->set_custom_minimum_size(Size2(200, 0));
+ layers_selection_button->set_text_overrun_behavior(TextParagraph::OVERRUN_TRIM_ELLIPSIS);
+ layers_selection_button->set_tooltip(TTR("TileMap Layers"));
+ layers_selection_button->connect("item_selected", callable_mp(this, &TileMapEditor::_layers_selection_item_selected));
tile_map_toolbar->add_child(layers_selection_button);
- toogle_highlight_selected_layer_button = memnew(Button);
- toogle_highlight_selected_layer_button->set_flat(true);
- toogle_highlight_selected_layer_button->set_toggle_mode(true);
- toogle_highlight_selected_layer_button->set_pressed(true);
- toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
- toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer"));
- tile_map_toolbar->add_child(toogle_highlight_selected_layer_button);
+ toggle_highlight_selected_layer_button = memnew(Button);
+ toggle_highlight_selected_layer_button->set_flat(true);
+ toggle_highlight_selected_layer_button->set_toggle_mode(true);
+ toggle_highlight_selected_layer_button->set_pressed(true);
+ toggle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
+ toggle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer"));
+ tile_map_toolbar->add_child(toggle_highlight_selected_layer_button);
tile_map_toolbar->add_child(memnew(VSeparator));
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 7158ebff59..ff586ebbfe 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -40,6 +40,7 @@
#include "scene/gui/check_box.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/split_container.h"
@@ -268,14 +269,22 @@ private:
HashMap<Vector2i, TileMapCell> drag_modified;
// Painting
- HashMap<Vector2i, TileMapCell> _draw_terrains(const HashMap<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
+ HashMap<Vector2i, TileMapCell> _draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const;
+ HashMap<Vector2i, TileMapCell> _draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const;
HashMap<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
HashMap<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
RBSet<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
HashMap<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
+ enum SelectedType {
+ SELECTED_TYPE_CONNECT = 0,
+ SELECTED_TYPE_PATH,
+ SELECTED_TYPE_PATTERN,
+ };
+ SelectedType selected_type;
int selected_terrain_set = -1;
+ int selected_terrain = -1;
TileSet::TerrainsPattern selected_terrains_pattern;
void _update_selection();
@@ -319,12 +328,9 @@ private:
// Toolbar.
HBoxContainer *tile_map_toolbar = nullptr;
- PopupMenu *layers_selection_popup = nullptr;
- Button *layers_selection_button = nullptr;
- Button *toogle_highlight_selected_layer_button = nullptr;
- void _layers_selection_button_draw();
- void _layers_selection_button_pressed();
- void _layers_selection_id_pressed(int p_id);
+ OptionButton *layers_selection_button = nullptr;
+ Button *toggle_highlight_selected_layer_button = nullptr;
+ void _layers_selection_item_selected(int p_index);
Button *toggle_grid_button = nullptr;
void _on_grid_toggled(bool p_pressed);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index b87aedcf60..66459d3ef9 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2071,9 +2071,10 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
}
} else if (p_property == "terrain_set") {
int current_terrain_set = tile_data_proxy->get("terrain_set");
+ ADD_UNDO(tile_data_proxy, "terrain");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
+ if (tile_set->is_valid_terrain_peering_bit(current_terrain_set, bit)) {
ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
}
}
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index fb4a563992..de373e121b 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -505,7 +505,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
@@ -513,7 +513,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
} else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
}
}
@@ -607,9 +607,10 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
ADD_UNDO(tile_data, "terrain_set");
+ ADD_UNDO(tile_data, "terrain");
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
- if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ if (tile_data->is_valid_terrain_peering_bit(bit)) {
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 642973648e..ecf1bea6bb 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -3673,6 +3673,9 @@ void VisualShaderEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
+ graph->set_minimap_opacity(EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"));
+ graph->set_connection_lines_curvature(EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"));
+ _update_graph();
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -5329,7 +5332,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-e Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 Exponential."), { VisualShaderNodeFloatFunc::FUNC_EXP2 }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeFloatFunc::FUNC_FLOOR }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeFloatFunc::FUNC_FRAC }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeFloatFunc::FUNC_FRACT }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeFloatFunc::FUNC_INVERSE_SQRT }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Natural logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Base-2 logarithm."), { VisualShaderNodeFloatFunc::FUNC_LOG2 }, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -5513,16 +5516,16 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRACT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
add_options.push_back(AddOption("Fresnel", "Vector", "Functions", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length2D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length3D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Length4D", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D));