diff options
Diffstat (limited to 'editor/plugins')
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)); |