summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp41
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h4
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp452
-rw-r--r--editor/plugins/animation_player_editor_plugin.h66
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp6
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp65
-rw-r--r--editor/plugins/asset_library_editor_plugin.h3
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.cpp95
-rw-r--r--editor/plugins/baked_lightmap_editor_plugin.h39
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp206
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h1
-rw-r--r--editor/plugins/collision_polygon_2d_editor_plugin.cpp8
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.cpp2
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp22
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h4
-rw-r--r--editor/plugins/curve_editor_plugin.cpp6
-rw-r--r--editor/plugins/editor_preview_plugins.cpp45
-rw-r--r--editor/plugins/gi_probe_editor_plugin.cpp2
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.h4
-rw-r--r--editor/plugins/line_2d_editor_plugin.cpp8
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp2
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.cpp145
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.h14
-rw-r--r--editor/plugins/navigation_mesh_generator.cpp4
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.cpp8
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp13
-rw-r--r--editor/plugins/particles_editor_plugin.cpp50
-rw-r--r--editor/plugins/particles_editor_plugin.h1
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_2d_editor_plugin.h4
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp21
-rw-r--r--editor/plugins/script_editor_plugin.cpp70
-rw-r--r--editor/plugins/script_editor_plugin.h6
-rw-r--r--editor/plugins/script_text_editor.cpp253
-rw-r--r--editor/plugins/script_text_editor.h5
-rw-r--r--editor/plugins/shader_editor_plugin.cpp39
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp671
-rw-r--r--editor/plugins/spatial_editor_plugin.h75
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp1
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp118
-rw-r--r--editor/plugins/tile_map_editor_plugin.h8
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp1168
-rw-r--r--editor/plugins/tile_set_editor_plugin.h119
44 files changed, 3250 insertions, 628 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index 736e176ab8..ff72a5a25e 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -32,18 +32,21 @@
#include "canvas_item_editor_plugin.h"
#include "core/os/keyboard.h"
-AbstractPolygon2DEditor::Vertex::Vertex()
- : polygon(-1), vertex(-1) {
+AbstractPolygon2DEditor::Vertex::Vertex() :
+ polygon(-1),
+ vertex(-1) {
// invalid vertex
}
-AbstractPolygon2DEditor::Vertex::Vertex(int p_vertex)
- : polygon(-1), vertex(p_vertex) {
+AbstractPolygon2DEditor::Vertex::Vertex(int p_vertex) :
+ polygon(-1),
+ vertex(p_vertex) {
// vertex p_vertex of current wip polygon
}
-AbstractPolygon2DEditor::Vertex::Vertex(int p_polygon, int p_vertex)
- : polygon(p_polygon), vertex(p_vertex) {
+AbstractPolygon2DEditor::Vertex::Vertex(int p_polygon, int p_vertex) :
+ polygon(p_polygon),
+ vertex(p_vertex) {
// vertex p_vertex of polygon p_polygon
}
@@ -66,12 +69,14 @@ AbstractPolygon2DEditor::PosVertex::PosVertex() {
// invalid vertex
}
-AbstractPolygon2DEditor::PosVertex::PosVertex(const Vertex &p_vertex, const Vector2 &p_pos)
- : Vertex(p_vertex.polygon, p_vertex.vertex), pos(p_pos) {
+AbstractPolygon2DEditor::PosVertex::PosVertex(const Vertex &p_vertex, const Vector2 &p_pos) :
+ Vertex(p_vertex.polygon, p_vertex.vertex),
+ pos(p_pos) {
}
-AbstractPolygon2DEditor::PosVertex::PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos)
- : Vertex(p_polygon, p_vertex), pos(p_pos) {
+AbstractPolygon2DEditor::PosVertex::PosVertex(int p_polygon, int p_vertex, const Vector2 &p_pos) :
+ Vertex(p_polygon, p_vertex),
+ pos(p_pos) {
}
bool AbstractPolygon2DEditor::_is_empty() const {
@@ -167,7 +172,7 @@ void AbstractPolygon2DEditor::_menu_option(int p_option) {
} break;
case MODE_EDIT: {
- wip_active = false;
+ _wip_close();
mode = MODE_EDIT;
button_create->set_pressed(false);
button_edit->set_pressed(true);
@@ -175,7 +180,7 @@ void AbstractPolygon2DEditor::_menu_option(int p_option) {
} break;
case MODE_DELETE: {
- wip_active = false;
+ _wip_close();
mode = MODE_DELETE;
button_create->set_pressed(false);
button_edit->set_pressed(false);
@@ -224,6 +229,9 @@ void AbstractPolygon2DEditor::_wip_changed() {
}
void AbstractPolygon2DEditor::_wip_close() {
+ if (!wip_active)
+ return;
+
if (_is_line()) {
_set_polygon(0, wip);
@@ -490,15 +498,14 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
return false;
}
-void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
+void AbstractPolygon2DEditor::forward_draw_over_viewport(Control *p_overlay) {
if (!_get_node())
return;
Control *vpc = canvas_item_editor->get_viewport_control();
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
- Ref<Texture> default_handle = get_icon("EditorHandle", "EditorIcons");
- Ref<Texture> selected_handle = get_icon("EditorHandleSelected", "EditorIcons");
+ const Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
const Vertex active_point = get_active_point();
const int n_polygons = _get_polygon_count();
@@ -572,8 +579,8 @@ void AbstractPolygon2DEditor::forward_draw_over_canvas(Control *p_canvas) {
const Vector2 p = (vertex == edited_point) ? edited_point.pos : (points[i] + offset);
const Vector2 point = xform.xform(p);
- Ref<Texture> handle = vertex == active_point ? selected_handle : default_handle;
- vpc->draw_texture(handle, point - handle->get_size() * 0.5);
+ const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1);
+ vpc->draw_texture(handle, point - handle->get_size() * 0.5, modulate);
}
}
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 915fe0803e..545eff6ef4 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -136,7 +136,7 @@ protected:
public:
bool forward_gui_input(const Ref<InputEvent> &p_event);
- void forward_draw_over_canvas(Control *p_canvas);
+ void forward_draw_over_viewport(Control *p_overlay);
void edit(Node *p_polygon);
AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wip_destructive = true);
@@ -152,7 +152,7 @@ class AbstractPolygon2DEditorPlugin : public EditorPlugin {
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return polygon_editor->forward_gui_input(p_event); }
- virtual void forward_draw_over_canvas(Control *p_canvas) { polygon_editor->forward_draw_over_canvas(p_canvas); }
+ virtual void forward_draw_over_viewport(Control *p_overlay) { polygon_editor->forward_draw_over_viewport(p_overlay); }
bool has_main_screen() const { return false; }
virtual String get_name() const { return klass; }
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 2b9c625aa4..019e32f847 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -36,6 +36,12 @@
#include "os/keyboard.h"
#include "project_settings.h"
+// For onion skinning
+#include "editor/plugins/canvas_item_editor_plugin.h"
+#include "editor/plugins/spatial_editor_plugin.h"
+#include "scene/main/viewport.h"
+#include "servers/visual_server.h"
+
void AnimationPlayerEditor::_node_removed(Node *p_node) {
if (player && player == p_node) {
@@ -98,6 +104,8 @@ void AnimationPlayerEditor::_notification(int p_what) {
tool_anim->get_popup()->connect("id_pressed", this, "_animation_tool_menu");
+ onion_skinning->get_popup()->connect("id_pressed", this, "_onion_skinning_menu");
+
blend_editor.next->connect("item_selected", this, "_blend_editor_next_changed");
get_tree()->connect("node_removed", this, "_node_removed");
@@ -132,6 +140,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
resource_edit_anim->set_icon(get_icon("EditResource", "EditorIcons"));
pin->set_icon(get_icon("Pin", "EditorIcons"));
tool_anim->set_icon(get_icon("Tools", "EditorIcons"));
+ onion_skinning->set_icon(get_icon("Onion", "EditorIcons"));
} break;
}
@@ -809,6 +818,7 @@ void AnimationPlayerEditor::_update_player() {
resource_edit_anim->set_disabled(animlist.size() == 0);
save_anim->set_disabled(animlist.size() == 0);
tool_anim->set_disabled(player == NULL);
+ onion_skinning->set_disabled(player == NULL);
int active_idx = -1;
for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) {
@@ -855,6 +865,9 @@ void AnimationPlayerEditor::_update_player() {
void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
+ if (onion.enabled)
+ _start_onion_skinning();
+
if (player && pin->is_pressed())
return; //ignore, pinned
player = p_player;
@@ -869,6 +882,55 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
}
}
+void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) {
+
+ if (!onion.can_overlay)
+ return;
+
+ // Can happen on viewport resize, at least
+ if (!_are_onion_layers_valid())
+ return;
+
+ RID ci = p_overlay->get_canvas_item();
+ Rect2 src_rect = p_overlay->get_global_rect();
+ // Re-flip since captures are already flipped
+ src_rect.position.y = onion.capture_size.y - (src_rect.position.y + src_rect.size.y);
+ src_rect.size.y *= -1;
+
+ Rect2 dst_rect = Rect2(Point2(), p_overlay->get_size());
+
+ float alpha_step = 1.0 / (onion.steps + 1);
+
+ int cidx = 0;
+ if (onion.past) {
+ float alpha = 0;
+ do {
+ alpha += alpha_step;
+
+ if (onion.captures_valid[cidx]) {
+ VS::get_singleton()->canvas_item_add_texture_rect_region(
+ ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha));
+ }
+
+ cidx++;
+ } while (cidx < onion.steps);
+ }
+ if (onion.future) {
+ float alpha = 1;
+ int base_cidx = cidx;
+ do {
+ alpha -= alpha_step;
+
+ if (onion.captures_valid[cidx]) {
+ VS::get_singleton()->canvas_item_add_texture_rect_region(
+ ci, dst_rect, VS::get_singleton()->viewport_get_texture(onion.captures[cidx]), src_rect, Color(1, 1, 1, alpha));
+ }
+
+ cidx++;
+ } while (cidx < base_cidx + onion.steps); // In case there's the present capture at the end, skip it
+ }
+}
+
void AnimationPlayerEditor::_animation_duplicate() {
if (!animation->get_item_count())
@@ -1100,6 +1162,70 @@ void AnimationPlayerEditor::_animation_save_menu(int p_option) {
}
}
+void AnimationPlayerEditor::_onion_skinning_menu(int p_option) {
+
+ PopupMenu *menu = onion_skinning->get_popup();
+ int idx = menu->get_item_index(p_option);
+
+ switch (p_option) {
+
+ case ONION_SKINNING_ENABLE: {
+
+ onion.enabled = !onion.enabled;
+ menu->set_item_checked(idx, onion.enabled);
+
+ if (onion.enabled)
+ _start_onion_skinning();
+ else
+ _stop_onion_skinning();
+
+ } break;
+
+ case ONION_SKINNING_PAST: {
+
+ // Ensure at least one of past/future is checjed
+ onion.past = onion.future ? !onion.past : true;
+ menu->set_item_checked(idx, onion.past);
+ } break;
+
+ case ONION_SKINNING_FUTURE: {
+
+ // Ensure at least one of past/future is checjed
+ onion.future = onion.past ? !onion.future : true;
+ menu->set_item_checked(idx, onion.future);
+ } break;
+
+ case ONION_SKINNING_1_STEP: // Fall-through
+ case ONION_SKINNING_2_STEPS:
+ case ONION_SKINNING_3_STEPS: {
+
+ onion.steps = (p_option - ONION_SKINNING_1_STEP) + 1;
+ int one_frame_idx = menu->get_item_index(ONION_SKINNING_1_STEP);
+ for (int i = 0; i <= ONION_SKINNING_LAST_STEPS_OPTION - ONION_SKINNING_1_STEP; i++) {
+ menu->set_item_checked(one_frame_idx + i, onion.steps == i + 1);
+ }
+ } break;
+
+ case ONION_SKINNING_DIFFERENCES_ONLY: {
+
+ onion.differences_only = !onion.differences_only;
+ menu->set_item_checked(idx, onion.differences_only);
+ } break;
+
+ case ONION_SKINNING_FORCE_WHITE_MODULATE: {
+
+ onion.force_white_modulate = !onion.force_white_modulate;
+ menu->set_item_checked(idx, onion.force_white_modulate);
+ } break;
+
+ case ONION_SKINNING_INCLUDE_GIZMOS: {
+
+ onion.include_gizmos = !onion.include_gizmos;
+ menu->set_item_checked(idx, onion.include_gizmos);
+ } break;
+ }
+}
+
void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventKey> k = p_ev;
@@ -1126,6 +1252,237 @@ void AnimationPlayerEditor::_unhandled_key_input(const Ref<InputEvent> &p_ev) {
}
}
+void AnimationPlayerEditor::_editor_visibility_changed() {
+
+ if (is_visible()) {
+ _start_onion_skinning();
+ }
+}
+
+bool AnimationPlayerEditor::_are_onion_layers_valid() {
+
+ ERR_FAIL_COND_V(!onion.past && !onion.future, false);
+
+ Point2 capture_size = get_tree()->get_root()->get_size();
+ return onion.captures.size() == onion.get_needed_capture_count() && onion.capture_size == capture_size;
+}
+
+void AnimationPlayerEditor::_allocate_onion_layers() {
+
+ _free_onion_layers();
+
+ int captures = onion.get_needed_capture_count();
+ Point2 capture_size = get_tree()->get_root()->get_size();
+
+ onion.captures.resize(captures);
+ onion.captures_valid.resize(captures);
+
+ for (int i = 0; i < captures; i++) {
+ bool is_present = onion.differences_only && i == captures - 1;
+
+ // Each capture is a viewport with a canvas item attached that renders a full-size rect with the contents of the main viewport
+ onion.captures[i] = VS::get_singleton()->viewport_create();
+ VS::get_singleton()->viewport_set_usage(onion.captures[i], VS::VIEWPORT_USAGE_2D);
+ VS::get_singleton()->viewport_set_size(onion.captures[i], capture_size.width, capture_size.height);
+ VS::get_singleton()->viewport_set_update_mode(onion.captures[i], VS::VIEWPORT_UPDATE_ALWAYS);
+ VS::get_singleton()->viewport_set_transparent_background(onion.captures[i], !is_present);
+ VS::get_singleton()->viewport_set_vflip(onion.captures[i], true);
+ VS::get_singleton()->viewport_attach_canvas(onion.captures[i], onion.capture.canvas);
+ }
+
+ // Reset the capture canvas item to the current root viewport texture (defensive)
+ VS::get_singleton()->canvas_item_clear(onion.capture.canvas_item);
+ VS::get_singleton()->canvas_item_add_texture_rect(onion.capture.canvas_item, Rect2(Point2(), capture_size), get_tree()->get_root()->get_texture()->get_rid());
+
+ onion.capture_size = capture_size;
+}
+
+void AnimationPlayerEditor::_free_onion_layers() {
+
+ for (int i = 0; i < onion.captures.size(); i++) {
+ if (onion.captures[i].is_valid()) {
+ VS::get_singleton()->free(onion.captures[i]);
+ }
+ }
+ onion.captures.clear();
+ onion.captures_valid.clear();
+}
+
+void AnimationPlayerEditor::_prepare_onion_layers_1() {
+
+ // This would be called per viewport and we want to act once only
+ int64_t frame = get_tree()->get_frame();
+ if (frame == onion.last_frame)
+ return;
+
+ if (!onion.enabled || !is_processing() || !is_visible() || !get_player()) {
+ _stop_onion_skinning();
+ return;
+ }
+
+ onion.last_frame = frame;
+
+ // Refresh viewports with no onion layers overlaid
+ onion.can_overlay = false;
+ plugin->update_overlays();
+
+ if (player->is_playing())
+ return;
+
+ // And go to next step afterwards
+ call_deferred("_prepare_onion_layers_2");
+}
+
+void AnimationPlayerEditor::_prepare_onion_layers_2() {
+
+ Ref<Animation> anim = player->get_animation(player->get_current_animation());
+ if (!anim.is_valid())
+ return;
+
+ if (!_are_onion_layers_valid())
+ _allocate_onion_layers();
+
+ // Hide superfluous elements that would make the overlay unnecessary cluttered
+ Dictionary canvas_edit_state;
+ Dictionary spatial_edit_state;
+ if (SpatialEditor::get_singleton()->is_visible()) {
+ // 3D
+ spatial_edit_state = SpatialEditor::get_singleton()->get_state();
+ Dictionary new_state = spatial_edit_state.copy();
+ new_state["show_grid"] = false;
+ new_state["show_origin"] = false;
+ Array orig_vp = spatial_edit_state["viewports"];
+ Array vp;
+ vp.resize(4);
+ for (int i = 0; i < vp.size(); i++) {
+ Dictionary d = ((Dictionary)orig_vp[i]).copy();
+ d["use_environment"] = false;
+ d["doppler"] = false;
+ d["gizmos"] = onion.include_gizmos ? d["gizmos"] : Variant(false);
+ d["information"] = false;
+ vp[i] = d;
+ }
+ new_state["viewports"] = vp;
+ // TODO: Save/restore only affected entries
+ SpatialEditor::get_singleton()->set_state(new_state);
+ } else { // CanvasItemEditor
+ // 2D
+ canvas_edit_state = CanvasItemEditor::get_singleton()->get_state();
+ Dictionary new_state = canvas_edit_state.copy();
+ new_state["show_grid"] = false;
+ new_state["show_rulers"] = false;
+ new_state["show_guides"] = false;
+ new_state["show_helpers"] = false;
+ // TODO: Save/restore only affected entries
+ CanvasItemEditor::get_singleton()->set_state(new_state);
+ }
+
+ // Tweak the root viewport to ensure it's rendered before our target
+ RID root_vp = get_tree()->get_root()->get_viewport_rid();
+ Rect2 root_vp_screen_rect = get_tree()->get_root()->get_attach_to_screen_rect();
+ VS::get_singleton()->viewport_attach_to_screen(root_vp, Rect2());
+ VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_ALWAYS);
+
+ RID present_rid;
+ if (onion.differences_only) {
+ // Capture present scene as it is
+ VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, RID());
+ present_rid = onion.captures[onion.captures.size() - 1];
+ VS::get_singleton()->viewport_set_active(present_rid, true);
+ VS::get_singleton()->viewport_set_parent_viewport(root_vp, present_rid);
+ VS::get_singleton()->draw(false);
+ VS::get_singleton()->viewport_set_active(present_rid, false);
+ }
+
+ // Backup current animation state
+ AnimatedValuesBackup values_backup = player->backup_animated_values();
+ float cpos = player->get_current_animation_position();
+
+ // Render every past/future step with the capture shader
+
+ VS::get_singleton()->canvas_item_set_material(onion.capture.canvas_item, onion.capture.material->get_rid());
+ onion.capture.material->set_shader_param("bkg_color", GLOBAL_GET("rendering/environment/default_clear_color"));
+ onion.capture.material->set_shader_param("differences_only", onion.differences_only);
+ onion.capture.material->set_shader_param("present", onion.differences_only ? VS::get_singleton()->viewport_get_texture(present_rid) : RID());
+
+ int step_off_a = onion.past ? -onion.steps : 0;
+ int step_off_b = onion.future ? onion.steps : 0;
+ int cidx = 0;
+ onion.capture.material->set_shader_param("dir_color", onion.force_white_modulate ? Color(1, 1, 1) : Color(EDITOR_GET("editors/animation/onion_layers_past_color")));
+ for (int step_off = step_off_a; step_off <= step_off_b; step_off++) {
+
+ if (step_off == 0) {
+ // Skip present step and switch to the color of future
+ if (!onion.force_white_modulate)
+ onion.capture.material->set_shader_param("dir_color", EDITOR_GET("editors/animation/onion_layers_future_color"));
+ continue;
+ }
+
+ float pos = cpos + step_off * anim->get_step();
+
+ bool valid = anim->has_loop() || pos >= 0 && pos <= anim->get_length();
+ onion.captures_valid[cidx] = valid;
+ if (valid) {
+ player->seek(pos, true);
+ get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials
+ values_backup.update_skeletons(); // Needed for Skeletons
+
+ VS::get_singleton()->viewport_set_active(onion.captures[cidx], true);
+ VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]);
+ VS::get_singleton()->draw(false);
+ VS::get_singleton()->viewport_set_active(onion.captures[cidx], false);
+ }
+
+ cidx++;
+ }
+
+ // Restore root viewport
+ VS::get_singleton()->viewport_set_parent_viewport(root_vp, RID());
+ VS::get_singleton()->viewport_attach_to_screen(root_vp, root_vp_screen_rect);
+ VS::get_singleton()->viewport_set_update_mode(root_vp, VS::VIEWPORT_UPDATE_WHEN_VISIBLE);
+
+ // Restore animation state
+ // (Seeking with update=true wouldn't do the trick because the current value of the properties
+ // may not match their value for the current point in the animation)
+ player->seek(cpos, false);
+ player->restore_animated_values(values_backup);
+
+ // Restor state of main editors
+ if (SpatialEditor::get_singleton()->is_visible()) {
+ // 3D
+ SpatialEditor::get_singleton()->set_state(spatial_edit_state);
+ } else { // CanvasItemEditor
+ // 2D
+ CanvasItemEditor::get_singleton()->set_state(canvas_edit_state);
+ }
+
+ // Update viewports with skin layers overlaid for the actual engine loop render
+ onion.can_overlay = true;
+ plugin->update_overlays();
+}
+
+void AnimationPlayerEditor::_start_onion_skinning() {
+
+ // FIXME: Using "idle_frame" makes onion layers update one frame behing the current
+ if (!get_tree()->is_connected("idle_frame", this, "call_deferred")) {
+ get_tree()->connect("idle_frame", this, "call_deferred", varray("_prepare_onion_layers_1"));
+ }
+}
+
+void AnimationPlayerEditor::_stop_onion_skinning() {
+
+ if (get_tree()->is_connected("idle_frame", this, "call_deferred")) {
+
+ get_tree()->disconnect("idle_frame", this, "call_deferred");
+
+ _free_onion_layers();
+
+ // Clean up the overlay
+ onion.can_overlay = false;
+ plugin->update_overlays();
+ }
+}
+
void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input);
@@ -1165,6 +1522,10 @@ void AnimationPlayerEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &AnimationPlayerEditor::_unhandled_key_input);
ClassDB::bind_method(D_METHOD("_animation_tool_menu"), &AnimationPlayerEditor::_animation_tool_menu);
ClassDB::bind_method(D_METHOD("_animation_save_menu"), &AnimationPlayerEditor::_animation_save_menu);
+ ClassDB::bind_method(D_METHOD("_onion_skinning_menu"), &AnimationPlayerEditor::_onion_skinning_menu);
+ ClassDB::bind_method(D_METHOD("_editor_visibility_changed"), &AnimationPlayerEditor::_editor_visibility_changed);
+ ClassDB::bind_method(D_METHOD("_prepare_onion_layers_1"), &AnimationPlayerEditor::_prepare_onion_layers_1);
+ ClassDB::bind_method(D_METHOD("_prepare_onion_layers_2"), &AnimationPlayerEditor::_prepare_onion_layers_2);
}
AnimationPlayerEditor *AnimationPlayerEditor::singleton = NULL;
@@ -1173,8 +1534,10 @@ AnimationPlayer *AnimationPlayerEditor::get_player() const {
return player;
}
-AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
+
+AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin) {
editor = p_editor;
+ plugin = p_plugin;
singleton = this;
updating = false;
@@ -1301,6 +1664,29 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
//tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM);
hb->add_child(tool_anim);
+ onion_skinning = memnew(MenuButton);
+ //onion_skinning->set_flat(false);
+ onion_skinning->set_tooltip(TTR("Onion Skinning"));
+ onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE);
+ onion_skinning->get_popup()->add_separator();
+ onion_skinning->get_popup()->add_item(TTR("Directions"), -1);
+ onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true);
+ onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST);
+ onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
+ onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE);
+ onion_skinning->get_popup()->add_separator();
+ onion_skinning->get_popup()->add_item(TTR("Depth"), -1);
+ onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true);
+ onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
+ onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
+ onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
+ onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
+ onion_skinning->get_popup()->add_separator();
+ onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY);
+ onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE);
+ onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS);
+ hb->add_child(onion_skinning);
+
pin = memnew(ToolButton);
pin->set_toggle_mode(true);
hb->add_child(pin);
@@ -1387,6 +1773,68 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
key_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed");
_update_player();
+
+ // Onion skinning
+
+ key_editor->connect("visibility_changed", this, "_editor_visibility_changed");
+
+ onion.enabled = false;
+ onion.past = true;
+ onion.future = false;
+ onion.steps = 1;
+ onion.differences_only = false;
+ onion.force_white_modulate = false;
+ onion.include_gizmos = false;
+
+ onion.last_frame = 0;
+ onion.can_overlay = false;
+ onion.capture_size = Size2();
+ onion.capture.canvas = VS::get_singleton()->canvas_create();
+ onion.capture.canvas_item = VS::get_singleton()->canvas_item_create();
+ VS::get_singleton()->canvas_item_set_parent(onion.capture.canvas_item, onion.capture.canvas);
+
+ onion.capture.material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+
+ onion.capture.shader = Ref<Shader>(memnew(Shader));
+ onion.capture.shader->set_code(" \
+ shader_type canvas_item; \
+ \
+ uniform vec4 bkg_color; \
+ uniform vec4 dir_color; \
+ uniform bool differences_only; \
+ uniform sampler2D present; \
+ \
+ float zero_if_equal(vec4 a, vec4 b) { \
+ return smoothstep(0.0, 0.005, length(a.rgb - b.rgb) / sqrt(3.0)); \
+ } \
+ \
+ void fragment() { \
+ vec4 capture_samp = texture(TEXTURE, UV); \
+ vec4 present_samp = texture(present, UV); \
+ float bkg_mask = zero_if_equal(capture_samp, bkg_color); \
+ float diff_mask = 1.0 - zero_if_equal(present_samp, bkg_color); \
+ diff_mask = min(1.0, diff_mask + float(!differences_only)); \
+ COLOR = vec4(capture_samp.rgb * dir_color.rgb, bkg_mask * diff_mask); \
+ } \
+ ");
+ VS::get_singleton()->material_set_shader(onion.capture.material->get_rid(), onion.capture.shader->get_rid());
+}
+
+AnimationPlayerEditor::~AnimationPlayerEditor() {
+
+ _free_onion_layers();
+ VS::get_singleton()->free(onion.capture.canvas);
+ VS::get_singleton()->free(onion.capture.canvas_item);
+}
+
+void AnimationPlayerEditorPlugin::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+
+ set_force_draw_over_forwarding_enabled();
+ } break;
+ }
}
void AnimationPlayerEditorPlugin::edit(Object *p_object) {
@@ -1420,7 +1868,7 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) {
editor = p_node;
- anim_editor = memnew(AnimationPlayerEditor(editor));
+ anim_editor = memnew(AnimationPlayerEditor(editor, this));
anim_editor->set_undo_redo(editor->get_undo_redo());
editor->add_bottom_panel_item(TTR("Animation"), anim_editor);
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index cea6b05ed4..1a1e92d7b7 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -42,11 +42,13 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
class AnimationKeyEditor;
+class AnimationPlayerEditorPlugin;
class AnimationPlayerEditor : public VBoxContainer {
GDCLASS(AnimationPlayerEditor, VBoxContainer);
EditorNode *editor;
+ AnimationPlayerEditorPlugin *plugin;
AnimationPlayer *player;
enum {
@@ -56,6 +58,19 @@ class AnimationPlayerEditor : public VBoxContainer {
};
enum {
+ ONION_SKINNING_ENABLE,
+ ONION_SKINNING_PAST,
+ ONION_SKINNING_FUTURE,
+ ONION_SKINNING_1_STEP,
+ ONION_SKINNING_2_STEPS,
+ ONION_SKINNING_3_STEPS,
+ ONION_SKINNING_LAST_STEPS_OPTION = ONION_SKINNING_3_STEPS,
+ ONION_SKINNING_DIFFERENCES_ONLY,
+ ONION_SKINNING_FORCE_WHITE_MODULATE,
+ ONION_SKINNING_INCLUDE_GIZMOS,
+ };
+
+ enum {
ANIM_SAVE,
ANIM_SAVE_AS
};
@@ -84,6 +99,7 @@ class AnimationPlayerEditor : public VBoxContainer {
Button *blend_anim;
Button *remove_anim;
MenuButton *tool_anim;
+ MenuButton *onion_skinning;
ToolButton *pin;
SpinBox *frame;
LineEdit *scale;
@@ -115,6 +131,36 @@ class AnimationPlayerEditor : public VBoxContainer {
AnimationKeyEditor *key_editor;
+ // Onion skinning
+ struct {
+ // Settings
+ bool enabled;
+ bool past;
+ bool future;
+ int steps;
+ bool differences_only;
+ bool force_white_modulate;
+ bool include_gizmos;
+
+ int get_needed_capture_count() const {
+ // 'Differences only' needs a capture of the present
+ return (past && future ? 2 * steps : steps) + (differences_only ? 1 : 0);
+ }
+
+ // Rendering
+ int64_t last_frame;
+ int can_overlay;
+ Size2 capture_size;
+ Vector<RID> captures;
+ Vector<bool> captures_valid;
+ struct {
+ RID canvas;
+ RID canvas_item;
+ Ref<ShaderMaterial> material;
+ Ref<Shader> shader;
+ } capture;
+ } onion;
+
void _select_anim_by_name(const String &p_anim);
void _play_pressed();
void _play_from_pressed();
@@ -161,8 +207,19 @@ class AnimationPlayerEditor : public VBoxContainer {
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
void _animation_tool_menu(int p_option);
void _animation_save_menu(int p_option);
+ void _onion_skinning_menu(int p_option);
+
+ void _editor_visibility_changed();
+ bool _are_onion_layers_valid();
+ void _allocate_onion_layers();
+ void _free_onion_layers();
+ void _prepare_onion_layers_1();
+ void _prepare_onion_layers_2();
+ void _start_onion_skinning();
+ void _stop_onion_skinning();
AnimationPlayerEditor();
+ ~AnimationPlayerEditor();
protected:
void _notification(int p_what);
@@ -182,7 +239,9 @@ public:
void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
void edit(AnimationPlayer *p_player);
- AnimationPlayerEditor(EditorNode *p_editor);
+ void forward_force_draw_over_viewport(Control *p_overlay);
+
+ AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin);
};
class AnimationPlayerEditorPlugin : public EditorPlugin {
@@ -192,6 +251,9 @@ class AnimationPlayerEditorPlugin : public EditorPlugin {
AnimationPlayerEditor *anim_editor;
EditorNode *editor;
+protected:
+ void _notification(int p_what);
+
public:
virtual Dictionary get_state() const { return anim_editor->get_state(); }
virtual void set_state(const Dictionary &p_state) { anim_editor->set_state(p_state); }
@@ -202,6 +264,8 @@ public:
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
+ virtual void forward_force_draw_over_viewport(Control *p_overlay) { anim_editor->forward_force_draw_over_viewport(p_overlay); }
+
AnimationPlayerEditorPlugin(EditorNode *p_node);
~AnimationPlayerEditorPlugin();
};
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index 22d23e1c72..8fe6538653 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -1196,14 +1196,14 @@ void AnimationTreeEditor::_edit_filters() {
if (base) {
NodePath np = E->get();
- if (np.get_property() != StringName()) {
+ if (np.get_subname_count() == 1) {
Node *n = base->get_node(np);
Skeleton *s = Object::cast_to<Skeleton>(n);
if (s) {
String skelbase = E->get().substr(0, E->get().find(":"));
- int bidx = s->find_bone(np.get_property());
+ int bidx = s->find_bone(np.get_subname(0));
if (bidx != -1) {
int bparent = s->get_bone_parent(bidx);
@@ -1213,7 +1213,7 @@ void AnimationTreeEditor::_edit_filters() {
String bpn = skelbase + ":" + s->get_bone_name(bparent);
if (pm.has(bpn)) {
parent = pm[bpn];
- descr = np.get_property();
+ descr = np.get_subname(0);
}
} else {
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index d2e7feb6e1..f04bc04d92 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -47,9 +47,9 @@ void EditorAssetLibraryItem::configure(const String &p_title, int p_asset_id, co
for (int i = 0; i < 5; i++) {
if (i < p_rating)
- stars[i]->set_texture(get_icon("RatingStar", "EditorIcons"));
+ stars[i]->set_texture(get_icon("Favorites", "EditorIcons"));
else
- stars[i]->set_texture(get_icon("RatingNoStar", "EditorIcons"));
+ stars[i]->set_texture(get_icon("NonFavorite", "EditorIcons"));
}
}
@@ -273,15 +273,15 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
HBoxContainer *hbox = memnew(HBoxContainer);
vbox->add_child(hbox);
- vbox->add_constant_override("separation", 15);
+ vbox->add_constant_override("separation", 15 * EDSCALE);
VBoxContainer *desc_vbox = memnew(VBoxContainer);
hbox->add_child(desc_vbox);
- hbox->add_constant_override("separation", 15);
+ hbox->add_constant_override("separation", 15 * EDSCALE);
item = memnew(EditorAssetLibraryItem);
desc_vbox->add_child(item);
- desc_vbox->set_custom_minimum_size(Size2(300, 0));
+ desc_vbox->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
desc_bg = memnew(PanelContainer);
desc_vbox->add_child(desc_bg);
@@ -292,12 +292,12 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
desc_bg->add_child(description);
preview = memnew(TextureRect);
- preview->set_custom_minimum_size(Size2(640, 345));
+ preview->set_custom_minimum_size(Size2(640 * EDSCALE, 345 * EDSCALE));
hbox->add_child(preview);
previews_bg = memnew(PanelContainer);
vbox->add_child(previews_bg);
- previews_bg->set_custom_minimum_size(Size2(0, 85));
+ previews_bg->set_custom_minimum_size(Size2(0, 101 * EDSCALE));
previews = memnew(ScrollContainer);
previews_bg->add_child(previews);
@@ -340,7 +340,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
} break;
case HTTPRequest::RESULT_REQUEST_FAILED: {
error_text = TTR("Request failed, return code:") + " " + itos(p_code);
- status->set_text(TTR("Req. Failed."));
+ status->set_text(TTR("Request Failed."));
} break;
case HTTPRequest::RESULT_REDIRECT_LIMIT_REACHED: {
error_text = TTR("Request failed, too many redirects");
@@ -445,7 +445,7 @@ void EditorAssetLibraryItemDownload::_install() {
void EditorAssetLibraryItemDownload::_make_request() {
download->cancel_request();
- download->set_download_file(EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("tmp_asset_" + itos(asset_id)) + ".zip");
+ download->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip");
Error err = download->request(host);
if (err != OK) {
@@ -680,7 +680,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
PoolByteArray image_data = p_data;
if (use_cache) {
- String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
+ String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
FileAccess *file = FileAccess::open(cache_filename_base + ".data", FileAccess::READ);
@@ -702,15 +702,28 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByt
Ref<Image> image = Ref<Image>(memnew(Image(r.ptr(), len)));
if (!image->empty()) {
- float max_height = 10000;
switch (image_queue[p_queue_id].image_type) {
- case IMAGE_QUEUE_ICON: max_height = 80; break;
- case IMAGE_QUEUE_THUMBNAIL: max_height = 80; break;
- case IMAGE_QUEUE_SCREENSHOT: max_height = 345; break;
- }
- float scale_ratio = max_height / image->get_height();
- if (scale_ratio < 1) {
- image->resize(image->get_width() * scale_ratio, image->get_height() * scale_ratio, Image::INTERPOLATE_CUBIC);
+ case IMAGE_QUEUE_ICON:
+
+ image->resize(80 * EDSCALE, 80 * EDSCALE, Image::INTERPOLATE_CUBIC);
+
+ break;
+ case IMAGE_QUEUE_THUMBNAIL: {
+ float max_height = 85 * EDSCALE;
+
+ float scale_ratio = max_height / (image->get_height() * EDSCALE);
+ if (scale_ratio < 1) {
+ image->resize(image->get_width() * EDSCALE * scale_ratio, image->get_height() * EDSCALE * scale_ratio, Image::INTERPOLATE_CUBIC);
+ }
+ } break;
+ case IMAGE_QUEUE_SCREENSHOT: {
+ float max_height = 397 * EDSCALE;
+
+ float scale_ratio = max_height / (image->get_height() * EDSCALE);
+ if (scale_ratio < 1) {
+ image->resize(image->get_width() * EDSCALE * scale_ratio, image->get_height() * EDSCALE * scale_ratio, Image::INTERPOLATE_CUBIC);
+ }
+ } break;
}
Ref<ImageTexture> tex;
@@ -738,7 +751,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) {
for (int i = 0; i < headers.size(); i++) {
if (headers[i].findn("ETag:") == 0) { // Save etag
- String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
+ String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges();
FileAccess *file;
@@ -786,7 +799,7 @@ void EditorAssetLibrary::_update_image_queue() {
for (Map<int, ImageQueue>::Element *E = image_queue.front(); E; E = E->next()) {
if (!E->get().active && current_images < max_images) {
- String cache_filename_base = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp").plus_file("assetimage_" + E->get().image_url.md5_text());
+ String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text());
Vector<String> headers;
if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) {
@@ -902,6 +915,11 @@ void EditorAssetLibrary::_search(int p_page) {
_api_request("asset", REQUESTING_SEARCH, args);
}
+void EditorAssetLibrary::_search_text_entered(const String &p_text) {
+
+ _search();
+}
+
HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int p_page_len, int p_total_items, int p_current_items) {
HBoxContainer *hbc = memnew(HBoxContainer);
@@ -1255,6 +1273,10 @@ void EditorAssetLibrary::_install_external_asset(String p_zip_path, String p_tit
emit_signal("install_asset", p_zip_path, p_title);
}
+void EditorAssetLibrary::disable_community_support() {
+ support->get_popup()->set_item_checked(SUPPORT_COMMUNITY, false);
+}
+
void EditorAssetLibrary::_bind_methods() {
ClassDB::bind_method("_http_request_completed", &EditorAssetLibrary::_http_request_completed);
@@ -1263,6 +1285,7 @@ void EditorAssetLibrary::_bind_methods() {
ClassDB::bind_method("_select_category", &EditorAssetLibrary::_select_category);
ClassDB::bind_method("_image_request_completed", &EditorAssetLibrary::_image_request_completed);
ClassDB::bind_method("_search", &EditorAssetLibrary::_search, DEFVAL(0));
+ ClassDB::bind_method("_search_text_entered", &EditorAssetLibrary::_search_text_entered);
ClassDB::bind_method("_install_asset", &EditorAssetLibrary::_install_asset);
ClassDB::bind_method("_manage_plugins", &EditorAssetLibrary::_manage_plugins);
ClassDB::bind_method("_asset_open", &EditorAssetLibrary::_asset_open);
@@ -1292,7 +1315,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
filter = memnew(LineEdit);
search_hb->add_child(filter);
filter->set_h_size_flags(SIZE_EXPAND_FILL);
- filter->connect("text_entered", this, "_search");
+ filter->connect("text_entered", this, "_search_text_entered");
search = memnew(Button(TTR("Search")));
search->connect("pressed", this, "_search");
search_hb->add_child(search);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 35977f3949..5536fbb2ec 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -284,6 +284,7 @@ class EditorAssetLibrary : public PanelContainer {
void _search(int p_page = 0);
void _rerun_search(int p_ignore);
+ void _search_text_entered(const String &p_text = "");
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
void _http_request_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
void _http_download_completed(int p_status, int p_code, const PoolStringArray &headers, const PoolByteArray &p_data);
@@ -301,6 +302,8 @@ protected:
void _notification(int p_what);
public:
+ void disable_community_support();
+
EditorAssetLibrary(bool p_templates_only = false);
};
diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp
new file mode 100644
index 0000000000..08f4d06ef7
--- /dev/null
+++ b/editor/plugins/baked_lightmap_editor_plugin.cpp
@@ -0,0 +1,95 @@
+#include "baked_lightmap_editor_plugin.h"
+
+void BakedLightmapEditorPlugin::_bake() {
+
+ if (lightmap) {
+ BakedLightmap::BakeError err;
+ if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) {
+ err = lightmap->bake(lightmap);
+ } else {
+ err = lightmap->bake(lightmap->get_parent());
+ }
+
+ switch (err) {
+ case BakedLightmap::BAKE_ERROR_NO_SAVE_PATH:
+ EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene (for images to be saved in the same dir), or pick a save path from the BakedLightmap properties."));
+ break;
+ case BakedLightmap::BAKE_ERROR_NO_MESHES:
+ EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on."));
+ break;
+ case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE:
+ EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable."));
+ break;
+ defaut : {}
+ }
+ }
+}
+
+void BakedLightmapEditorPlugin::edit(Object *p_object) {
+
+ BakedLightmap *s = Object::cast_to<BakedLightmap>(p_object);
+ if (!s)
+ return;
+
+ lightmap = s;
+}
+
+bool BakedLightmapEditorPlugin::handles(Object *p_object) const {
+
+ return p_object->is_class("BakedLightmap");
+}
+
+void BakedLightmapEditorPlugin::make_visible(bool p_visible) {
+
+ if (p_visible) {
+ bake->show();
+ } else {
+
+ bake->hide();
+ }
+}
+
+EditorProgress *BakedLightmapEditorPlugin::tmp_progress = NULL;
+
+void BakedLightmapEditorPlugin::bake_func_begin(int p_steps) {
+
+ ERR_FAIL_COND(tmp_progress != NULL);
+
+ tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), p_steps, true));
+}
+
+bool BakedLightmapEditorPlugin::bake_func_step(int p_step, const String &p_description) {
+
+ ERR_FAIL_COND_V(tmp_progress == NULL, false);
+ return tmp_progress->step(p_description, p_step);
+}
+
+void BakedLightmapEditorPlugin::bake_func_end() {
+ ERR_FAIL_COND(tmp_progress == NULL);
+ memdelete(tmp_progress);
+ tmp_progress = NULL;
+}
+
+void BakedLightmapEditorPlugin::_bind_methods() {
+
+ ClassDB::bind_method("_bake", &BakedLightmapEditorPlugin::_bake);
+}
+
+BakedLightmapEditorPlugin::BakedLightmapEditorPlugin(EditorNode *p_node) {
+
+ editor = p_node;
+ bake = memnew(Button);
+ bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
+ bake->set_text(TTR("Bake Lightmaps"));
+ bake->hide();
+ bake->connect("pressed", this, "_bake");
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake);
+ lightmap = NULL;
+
+ BakedLightmap::bake_begin_function = bake_func_begin;
+ BakedLightmap::bake_step_function = bake_func_step;
+ BakedLightmap::bake_end_function = bake_func_end;
+}
+
+BakedLightmapEditorPlugin::~BakedLightmapEditorPlugin() {
+}
diff --git a/editor/plugins/baked_lightmap_editor_plugin.h b/editor/plugins/baked_lightmap_editor_plugin.h
new file mode 100644
index 0000000000..d64c33884a
--- /dev/null
+++ b/editor/plugins/baked_lightmap_editor_plugin.h
@@ -0,0 +1,39 @@
+#ifndef BAKED_LIGHTMAP_EDITOR_PLUGIN_H
+#define BAKED_LIGHTMAP_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/baked_lightmap.h"
+#include "scene/resources/material.h"
+
+class BakedLightmapEditorPlugin : public EditorPlugin {
+
+ GDCLASS(BakedLightmapEditorPlugin, EditorPlugin);
+
+ BakedLightmap *lightmap;
+
+ Button *bake;
+ EditorNode *editor;
+
+ static EditorProgress *tmp_progress;
+ static void bake_func_begin(int p_steps);
+ static bool bake_func_step(int p_step, const String &p_description);
+ static void bake_func_end();
+
+ void _bake();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_name() const { return "BakedLightmap"; }
+ bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ BakedLightmapEditorPlugin(EditorNode *p_node);
+ ~BakedLightmapEditorPlugin();
+};
+
+#endif // BAKED_LIGHTMAP_EDITOR_PLUGIN_H
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 38467369db..ad22c12372 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -69,8 +69,8 @@ class SnapDialog : public ConfirmationDialog {
SpinBox *rotation_step;
public:
- SnapDialog()
- : ConfirmationDialog() {
+ SnapDialog() :
+ ConfirmationDialog() {
const int SPIN_BOX_GRID_RANGE = 256;
const int SPIN_BOX_ROTATION_RANGE = 360;
Label *label;
@@ -176,9 +176,9 @@ void CanvasItemEditor::_edit_set_pivot(const Vector2 &mouse_pos) {
for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
Node2D *n2d = Object::cast_to<Node2D>(E->get());
- if (n2d && n2d->edit_has_pivot()) {
+ if (n2d && n2d->_edit_use_pivot()) {
- Vector2 offset = n2d->edit_get_pivot();
+ Vector2 offset = n2d->_edit_get_pivot();
Vector2 gpos = n2d->get_global_position();
Vector2 local_mouse_pos = n2d->get_canvas_transform().affine_inverse().xform(mouse_pos);
@@ -186,9 +186,9 @@ void CanvasItemEditor::_edit_set_pivot(const Vector2 &mouse_pos) {
Vector2 motion_ofs = gpos - local_mouse_pos;
undo_redo->add_do_method(n2d, "set_global_position", local_mouse_pos);
- undo_redo->add_do_method(n2d, "edit_set_pivot", offset + n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs));
+ undo_redo->add_do_method(n2d, "_edit_set_pivot", offset + n2d->get_global_transform().affine_inverse().basis_xform(motion_ofs));
undo_redo->add_undo_method(n2d, "set_global_position", gpos);
- undo_redo->add_undo_method(n2d, "edit_set_pivot", offset);
+ undo_redo->add_undo_method(n2d, "_edit_set_pivot", offset);
for (int i = 0; i < n2d->get_child_count(); i++) {
Node2D *n2dc = Object::cast_to<Node2D>(n2d->get_child(i));
if (!n2dc)
@@ -249,8 +249,8 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
Transform2D ci_transform = canvas_item->get_global_transform_with_canvas();
Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D();
if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
- Point2 begin = ci_transform.xform(canvas_item->get_item_rect().get_position());
- Point2 end = ci_transform.xform(canvas_item->get_item_rect().get_position() + canvas_item->get_item_rect().get_size());
+ Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
+ Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
_snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
_snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
@@ -282,8 +282,8 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
can_snap = true;
} else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
- begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->get_item_rect().get_position());
- end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->get_item_rect().get_position() + parent_ci->get_item_rect().get_size());
+ begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
+ end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
can_snap = true;
}
@@ -306,8 +306,8 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
// Self sides (for anchors)
if ((snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->get_item_rect().get_position());
- end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->get_item_rect().get_position() + p_canvas_item->get_item_rect().get_size());
+ begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
+ end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
_snap_if_closer_point(p_target, begin, output, snapped, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation);
}
@@ -629,7 +629,7 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) {
- Rect2 rect = c->get_item_rect();
+ Rect2 rect = c->_edit_get_rect();
Point2 local_pos = (p_parent_xform * p_canvas_xform * c->get_transform()).affine_inverse().xform(p_pos);
if (rect.has_point(local_pos)) {
@@ -675,7 +675,7 @@ void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_n
if (c && c->is_visible_in_tree() && !c->has_meta("_edit_lock_") && !Object::cast_to<CanvasLayer>(c)) {
- Rect2 rect = c->get_item_rect();
+ Rect2 rect = c->_edit_get_rect();
Transform2D xform = p_parent_xform * p_canvas_xform * c->get_transform();
if (p_rect.has_point(xform.xform(rect.position)) &&
@@ -767,15 +767,15 @@ void CanvasItemEditor::_key_move(const Vector2 &p_dir, bool p_snap, KeyMoveMODE
if (p_snap)
drag *= grid_step * Math::pow(2.0, grid_step_multiplier);
- undo_redo->add_undo_method(canvas_item, "edit_set_state", canvas_item->edit_get_state());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state());
if (p_move_mode == MOVE_VIEW_BASE) {
// drag = transform.affine_inverse().basis_xform(p_dir); // zoom sensitive
drag = canvas_item->get_global_transform_with_canvas().affine_inverse().basis_xform(drag);
- Rect2 local_rect = canvas_item->get_item_rect();
+ Rect2 local_rect = canvas_item->_edit_get_rect();
local_rect.position += drag;
- undo_redo->add_do_method(canvas_item, "edit_set_rect", local_rect);
+ undo_redo->add_do_method(canvas_item, "_edit_set_rect", local_rect);
} else { // p_move_mode==MOVE_LOCAL_BASE || p_move_mode==MOVE_LOCAL_WITH_ROT
@@ -816,7 +816,7 @@ Point2 CanvasItemEditor::_find_topleftmost_point() {
if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root())
continue;
- Rect2 rect = canvas_item->get_item_rect();
+ Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xform = canvas_item->get_global_transform_with_canvas();
r2.expand_to(xform.xform(rect.position));
@@ -877,7 +877,7 @@ CanvasItemEditor::DragType CanvasItemEditor::_get_resize_handle_drag_type(const
ERR_FAIL_COND_V(!canvas_item, DRAG_NONE);
- Rect2 rect = canvas_item->get_item_rect();
+ Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xforml = canvas_item->get_global_transform_with_canvas();
Transform2D xform = transform * xforml;
@@ -1011,14 +1011,14 @@ void CanvasItemEditor::_prepare_drag(const Point2 &p_click_pos) {
if (!se)
continue;
- se->undo_state = canvas_item->edit_get_state();
+ se->undo_state = canvas_item->_edit_get_state();
if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->edit_get_pivot();
+ se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
if (Object::cast_to<Control>(canvas_item))
se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->get_item_rect();
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
}
if (selection.size() == 1 && Object::cast_to<Node2D>(selection[0]) && bone_ik_list.size() == 0) {
@@ -1442,6 +1442,22 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
}
}
+ Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
+ if (magnify_gesture.is_valid()) {
+
+ _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position());
+ return;
+ }
+
+ Ref<InputEventPanGesture> pan_gesture = p_event;
+ if (pan_gesture.is_valid()) {
+
+ const Vector2 delta = (int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom) * pan_gesture->get_delta();
+ h_scroll->set_value(h_scroll->get_value() + delta.x);
+ v_scroll->set_value(v_scroll->get_value() + delta.y);
+ return;
+ }
+
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
// Button event
@@ -1500,7 +1516,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
// Cancel a drag
if (bone_ik_list.size()) {
for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) {
- E->get().node->edit_set_state(E->get().orig_state);
+ E->get().node->_edit_set_state(E->get().orig_state);
}
bone_ik_list.clear();
@@ -1519,9 +1535,9 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
if (!se)
continue;
- canvas_item->edit_set_state(se->undo_state);
+ canvas_item->_edit_set_state(se->undo_state);
if (Object::cast_to<Node2D>(canvas_item))
- Object::cast_to<Node2D>(canvas_item)->edit_set_pivot(se->undo_pivot);
+ Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot);
if (Object::cast_to<Control>(canvas_item))
Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot);
}
@@ -1574,8 +1590,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
for (List<BoneIK>::Element *E = bone_ik_list.back(); E; E = E->prev()) {
- undo_redo->add_do_method(E->get().node, "edit_set_state", E->get().node->edit_get_state());
- undo_redo->add_undo_method(E->get().node, "edit_set_state", E->get().orig_state);
+ undo_redo->add_do_method(E->get().node, "_edit_set_state", E->get().node->_edit_get_state());
+ undo_redo->add_undo_method(E->get().node, "_edit_set_state", E->get().orig_state);
}
undo_redo->add_do_method(viewport, "update");
@@ -1601,14 +1617,14 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
if (!se)
continue;
- Variant state = canvas_item->edit_get_state();
- undo_redo->add_do_method(canvas_item, "edit_set_state", state);
- undo_redo->add_undo_method(canvas_item, "edit_set_state", se->undo_state);
+ Variant state = canvas_item->_edit_get_state();
+ undo_redo->add_do_method(canvas_item, "_edit_set_state", state);
+ undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state);
{
Node2D *pvt = Object::cast_to<Node2D>(canvas_item);
- if (pvt && pvt->edit_has_pivot()) {
- undo_redo->add_do_method(canvas_item, "edit_set_pivot", pvt->edit_get_pivot());
- undo_redo->add_undo_method(canvas_item, "edit_set_pivot", se->undo_pivot);
+ if (pvt && pvt->_edit_use_pivot()) {
+ undo_redo->add_do_method(canvas_item, "_edit_set_pivot", pvt->_edit_get_pivot());
+ undo_redo->add_undo_method(canvas_item, "_edit_set_pivot", se->undo_pivot);
}
Control *cnt = Object::cast_to<Control>(canvas_item);
@@ -1709,7 +1725,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
BoneIK bik;
bik.node = b;
bik.len = len;
- bik.orig_state = b->edit_get_state();
+ bik.orig_state = b->_edit_get_state();
bone_ik_list.push_back(bik);
@@ -1741,13 +1757,13 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
if ((b->get_control() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) {
drag = DRAG_ROTATE;
drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->edit_get_state();
+ se->undo_state = canvas_item->_edit_get_state();
if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->edit_get_pivot();
+ se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
if (Object::cast_to<Control>(canvas_item))
se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->get_item_rect();
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
return;
}
@@ -1764,13 +1780,13 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
drag = _get_resize_handle_drag_type(click, drag_point_from);
if (drag != DRAG_NONE) {
drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->edit_get_state();
+ se->undo_state = canvas_item->_edit_get_state();
if (Object::cast_to<Node2D>(canvas_item))
- se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->edit_get_pivot();
+ se->undo_pivot = Object::cast_to<Node2D>(canvas_item)->_edit_get_pivot();
if (Object::cast_to<Control>(canvas_item))
se->undo_pivot = Object::cast_to<Control>(canvas_item)->get_pivot_offset();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->get_item_rect();
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
return;
}
@@ -1780,9 +1796,9 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
drag = _get_anchor_handle_drag_type(click, drag_point_from);
if (drag != DRAG_NONE) {
drag_from = transform.affine_inverse().xform(click);
- se->undo_state = canvas_item->edit_get_state();
+ se->undo_state = canvas_item->_edit_get_state();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->get_item_rect();
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
return;
}
}
@@ -1858,7 +1874,17 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
}
if (drag == DRAG_NONE) {
- if (((m->get_button_mask() & BUTTON_MASK_LEFT) && tool == TOOL_PAN) || (m->get_button_mask() & BUTTON_MASK_MIDDLE) || ((m->get_button_mask() & BUTTON_MASK_LEFT) && Input::get_singleton()->is_key_pressed(KEY_SPACE))) {
+ bool space_pressed = Input::get_singleton()->is_key_pressed(KEY_SPACE);
+ bool simple_panning = EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning");
+ int button = m->get_button_mask();
+
+ // Check if any of the panning triggers are activated
+ bool panning_tool = (button & BUTTON_MASK_LEFT) && tool == TOOL_PAN;
+ bool panning_middle_button = button & BUTTON_MASK_MIDDLE;
+ bool panning_spacebar = (button & BUTTON_MASK_LEFT) && space_pressed;
+ bool panning_spacebar_simple = space_pressed && simple_panning;
+
+ if (panning_tool || panning_middle_button || panning_spacebar || panning_spacebar_simple) {
// Pan the viewport
Point2i relative;
if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
@@ -1890,9 +1916,9 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
bool dragging_bone = drag == DRAG_ALL && selection.size() == 1 && bone_ik_list.size();
if (!dragging_bone) {
- canvas_item->edit_set_state(se->undo_state); //reset state and reapply
+ canvas_item->_edit_set_state(se->undo_state); //reset state and reapply
if (Object::cast_to<Node2D>(canvas_item))
- Object::cast_to<Node2D>(canvas_item)->edit_set_pivot(se->undo_pivot);
+ Object::cast_to<Node2D>(canvas_item)->_edit_set_pivot(se->undo_pivot);
if (Object::cast_to<Control>(canvas_item))
Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot);
}
@@ -2003,10 +2029,10 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dfrom);
- Rect2 local_rect = canvas_item->get_item_rect();
+ Rect2 local_rect = canvas_item->_edit_get_rect();
Vector2 begin = local_rect.position;
Vector2 end = local_rect.position + local_rect.size;
- Vector2 minsize = canvas_item->edit_get_minimum_size();
+ Vector2 minsize = canvas_item->_edit_get_minimum_size();
if (uniform) {
// Keep the height/width ratio of the item
@@ -2084,7 +2110,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
if (Object::cast_to<Node2D>(canvas_item)) {
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
- n2d->edit_set_pivot(se->undo_pivot + drag_vector);
+ n2d->_edit_set_pivot(se->undo_pivot + drag_vector);
}
if (Object::cast_to<Control>(canvas_item)) {
Object::cast_to<Control>(canvas_item)->set_pivot_offset(se->undo_pivot + drag_vector);
@@ -2103,7 +2129,7 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
local_rect.position = begin;
local_rect.size = end - begin;
- canvas_item->edit_set_rect(local_rect);
+ canvas_item->_edit_set_rect(local_rect);
} else {
//ok, all that had to be done was done, now solve IK
@@ -2268,7 +2294,7 @@ void CanvasItemEditor::_draw_focus() {
void CanvasItemEditor::_draw_guides() {
- Color guide_color = Color(0.6, 0.0, 0.8);
+ Color guide_color = EditorSettings::get_singleton()->get("editors/2d/guides_color");
Transform2D xform = viewport_scrollable->get_transform() * transform;
// Guides already there
@@ -2454,7 +2480,7 @@ void CanvasItemEditor::_draw_selection() {
if (!se)
continue;
- Rect2 rect = canvas_item->get_item_rect();
+ Rect2 rect = canvas_item->_edit_get_rect();
if (show_helpers && drag != DRAG_NONE && drag != DRAG_PIVOT) {
const Transform2D pre_drag_xform = transform * se->pre_drag_xform;
@@ -2496,7 +2522,7 @@ void CanvasItemEditor::_draw_selection() {
Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
if (node2d) {
- if (node2d->edit_has_pivot()) {
+ if (node2d->_edit_use_pivot()) {
viewport->draw_texture(pivot_icon, xform.get_origin() + (-pivot_icon->get_size() / 2).floor());
can_move_pivot = true;
pivot_found = true;
@@ -2868,7 +2894,7 @@ void CanvasItemEditor::_get_encompassing_rect(Node *p_node, Rect2 &r_rect, const
CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
if (c && c->is_visible_in_tree()) {
- Rect2 rect = c->get_item_rect();
+ Rect2 rect = c->_edit_get_rect();
Transform2D xform = p_xform * c->get_transform();
r_rect.expand_to(xform.xform(rect.position));
r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
@@ -2929,8 +2955,13 @@ void CanvasItemEditor::_draw_viewport() {
EditorPluginList *over_plugin_list = editor->get_editor_plugins_over();
if (!over_plugin_list->empty()) {
- over_plugin_list->forward_draw_over_canvas(viewport);
+ over_plugin_list->forward_draw_over_viewport(viewport);
+ }
+ EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over();
+ if (!force_over_plugin_list->empty()) {
+ force_over_plugin_list->forward_force_draw_over_viewport(viewport);
}
+
_draw_bones();
}
@@ -2963,7 +2994,7 @@ void CanvasItemEditor::_notification(int p_what) {
if (!se)
continue;
- Rect2 r = canvas_item->get_item_rect();
+ Rect2 r = canvas_item->_edit_get_rect();
Transform2D xform = canvas_item->get_transform();
if (r != se->prev_rect || xform != se->prev_xform) {
@@ -3899,7 +3930,7 @@ void CanvasItemEditor::_focus_selection(int p_op) {
//if (!canvas_item->is_visible_in_tree()) continue;
++count;
- Rect2 item_rect = canvas_item->get_item_rect();
+ Rect2 item_rect = canvas_item->_edit_get_rect();
Vector2 pos = canvas_item->get_global_transform().get_origin();
Vector2 scale = canvas_item->get_global_transform().get_scale();
@@ -4283,7 +4314,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
show_grid = false;
show_helpers = false;
- show_rulers = false;
+ show_rulers = true;
show_guides = true;
zoom = 1;
grid_offset = Point2();
@@ -4311,6 +4342,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
drag = DRAG_NONE;
bone_last_frame = 0;
additive_selection = false;
+
+ // Update the menus checkboxes
+ call_deferred("set_state", get_state());
}
CanvasItemEditor *CanvasItemEditor::singleton = NULL;
@@ -4455,15 +4489,24 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Ref<Texture> texture = Ref<Texture>(Object::cast_to<Texture>(ResourceCache::get(path)));
Size2 texture_size = texture->get_size();
- editor_data->get_undo_redo().add_do_method(parent, "add_child", child);
- editor_data->get_undo_redo().add_do_method(child, "set_owner", editor->get_edited_scene());
- editor_data->get_undo_redo().add_do_reference(child);
- editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child);
+ if (parent) {
+ editor_data->get_undo_redo().add_do_method(parent, "add_child", child);
+ editor_data->get_undo_redo().add_do_method(child, "set_owner", editor->get_edited_scene());
+ editor_data->get_undo_redo().add_do_reference(child);
+ editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child);
+ } else { // if we haven't parent, lets try to make a child as a parent.
+ editor_data->get_undo_redo().add_do_method(editor, "set_edited_scene", child);
+ editor_data->get_undo_redo().add_do_method(child, "set_owner", editor->get_edited_scene());
+ editor_data->get_undo_redo().add_do_reference(child);
+ editor_data->get_undo_redo().add_undo_method(editor, "set_edited_scene", (Object *)NULL);
+ }
- String new_name = parent->validate_child_name(child);
- ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
- editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
- editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ if (parent) {
+ String new_name = parent->validate_child_name(child);
+ ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ editor_data->get_undo_redo().add_do_method(sed, "live_debug_create_node", editor->get_edited_scene()->get_path_to(parent), child->get_class(), new_name);
+ editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
+ }
// handle with different property for texture
String property = "texture";
@@ -4496,8 +4539,8 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
}
// locate at preview position
- Point2 pos;
- if (parent->has_method("get_global_position")) {
+ Point2 pos = Point2(0, 0);
+ if (parent && parent->has_method("get_global_position")) {
pos = parent->call("get_global_position");
}
Transform2D trans = canvas->get_canvas_transform();
@@ -4658,6 +4701,18 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
return false;
}
+void CanvasItemEditorViewport::_show_resource_type_selector() {
+ List<BaseButton *> btn_list;
+ button_group->get_buttons(&btn_list);
+
+ for (int i = 0; i < btn_list.size(); i++) {
+ CheckBox *check = Object::cast_to<CheckBox>(btn_list[i]);
+ check->set_pressed(check->get_text() == default_type);
+ }
+ selector->set_title(vformat(TTR("Add %s"), default_type));
+ selector->popup_centered_minsize();
+}
+
void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) {
bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT);
@@ -4674,10 +4729,9 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
if (root_node) {
list.push_back(root_node);
} else {
- accept->get_ok()->set_text(TTR("OK :("));
- accept->set_text(TTR("No parent to instance a child at."));
- accept->popup_centered_minsize();
- _remove_preview();
+ drop_pos = p_point;
+ target_node = NULL;
+ _show_resource_type_selector();
return;
}
}
@@ -4696,15 +4750,7 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p
drop_pos = p_point;
if (is_alt) {
- List<BaseButton *> btn_list;
- button_group->get_buttons(&btn_list);
-
- for (int i = 0; i < btn_list.size(); i++) {
- CheckBox *check = Object::cast_to<CheckBox>(btn_list[i]);
- check->set_pressed(check->get_text() == default_type);
- }
- selector->set_title(vformat(TTR("Add %s"), default_type));
- selector->popup_centered_minsize();
+ _show_resource_type_selector();
} else {
_perform_drop_data();
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 457833e1a7..4be09a16e9 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -578,6 +578,7 @@ class CanvasItemEditorViewport : public Control {
void _create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point);
bool _create_instance(Node *parent, String &path, const Point2 &p_point);
void _perform_drop_data();
+ void _show_resource_type_selector();
static void _bind_methods();
diff --git a/editor/plugins/collision_polygon_2d_editor_plugin.cpp b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
index 00e6d617a1..6ac80caf94 100644
--- a/editor/plugins/collision_polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_2d_editor_plugin.cpp
@@ -39,10 +39,10 @@ void CollisionPolygon2DEditor::_set_node(Node *p_polygon) {
node = Object::cast_to<CollisionPolygon2D>(p_polygon);
}
-CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor)
- : AbstractPolygon2DEditor(p_editor) {
+CollisionPolygon2DEditor::CollisionPolygon2DEditor(EditorNode *p_editor) :
+ AbstractPolygon2DEditor(p_editor) {
}
-CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node)
- : AbstractPolygon2DEditorPlugin(p_node, memnew(CollisionPolygon2DEditor(p_node)), "CollisionPolygon2D") {
+CollisionPolygon2DEditorPlugin::CollisionPolygon2DEditorPlugin(EditorNode *p_node) :
+ AbstractPolygon2DEditorPlugin(p_node, memnew(CollisionPolygon2DEditor(p_node)), "CollisionPolygon2D") {
}
diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp
index 24c4813771..0818c8975e 100644
--- a/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -389,7 +389,7 @@ void CollisionPolygonEditor::_polygon_draw() {
rect = rect.grow(1);
- Rect3 r;
+ AABB r;
r.position.x = rect.position.x;
r.position.y = rect.position.y;
r.position.z = depth;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index 005de096cd..029e3a558d 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -414,7 +414,7 @@ void CollisionShape2DEditor::_get_current_shape_type() {
canvas_item_editor->get_viewport_control()->update();
}
-void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
+void CollisionShape2DEditor::forward_draw_over_viewport(Control *p_overlay) {
if (!node) {
return;
@@ -448,8 +448,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles[0] = Point2(radius, -height);
handles[1] = Point2(0, -(height + radius));
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
- p_canvas->draw_texture(h, gt.xform(handles[1]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
} break;
@@ -459,7 +459,7 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles.resize(1);
handles[0] = Point2(shape->get_radius(), 0);
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
} break;
@@ -478,8 +478,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles[0] = shape->get_normal() * shape->get_d();
handles[1] = shape->get_normal() * (shape->get_d() + 30.0);
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
- p_canvas->draw_texture(h, gt.xform(handles[1]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
} break;
@@ -489,7 +489,7 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles.resize(1);
handles[0] = Point2(0, shape->get_length());
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
} break;
@@ -501,8 +501,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles[0] = Point2(ext.x, 0);
handles[1] = Point2(0, -ext.y);
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
- p_canvas->draw_texture(h, gt.xform(handles[1]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
} break;
@@ -513,8 +513,8 @@ void CollisionShape2DEditor::forward_draw_over_canvas(Control *p_canvas) {
handles[0] = shape->get_a();
handles[1] = shape->get_b();
- p_canvas->draw_texture(h, gt.xform(handles[0]) - size);
- p_canvas->draw_texture(h, gt.xform(handles[1]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[0]) - size);
+ p_overlay->draw_texture(h, gt.xform(handles[1]) - size);
} break;
}
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index d4fbe87fb3..1e930ef371 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -74,7 +74,7 @@ protected:
public:
bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
- void forward_draw_over_canvas(Control *p_canvas);
+ void forward_draw_over_viewport(Control *p_overlay);
void edit(Node *p_node);
CollisionShape2DEditor(EditorNode *p_editor);
@@ -88,7 +88,7 @@ class CollisionShape2DEditorPlugin : public EditorPlugin {
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return collision_shape_2d_editor->forward_canvas_gui_input(p_event); }
- virtual void forward_draw_over_canvas(Control *p_canvas) { return collision_shape_2d_editor->forward_draw_over_canvas(p_canvas); }
+ virtual void forward_draw_over_viewport(Control *p_overlay) { return collision_shape_2d_editor->forward_draw_over_viewport(p_overlay); }
virtual String get_name() const { return "CollisionShape2D"; }
bool has_main_screen() const { return false; }
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 2754aeed06..f77016c1d6 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -586,8 +586,10 @@ struct CanvasItemPlotCurve {
Color color1;
Color color2;
- CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2)
- : ci(p_ci), color1(p_color1), color2(p_color2) {}
+ CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2) :
+ ci(p_ci),
+ color1(p_color1),
+ color2(p_color2) {}
void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
ci.draw_line(pos0, pos1, in_definition ? color1 : color2);
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 5f73d0b465..558f44769d 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -184,7 +184,7 @@ Ref<Texture> EditorPackedScenePreviewPlugin::generate(const RES &p_from) {
Ref<Texture> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path) {
- String temp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp");
+ String temp_path = EditorSettings::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
cache_base = temp_path.plus_file("resthumb-" + cache_base);
@@ -235,29 +235,34 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from) {
Ref<Material> material = p_from;
ERR_FAIL_COND_V(material.is_null(), Ref<Texture>());
- VS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
+ if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
- VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
+ VS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
- preview_done = false;
- VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant());
+ VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture
- while (!preview_done) {
- OS::get_singleton()->delay_usec(10);
- }
+ preview_done = false;
+ VS::get_singleton()->request_frame_drawn_callback(this, "_preview_done", Variant());
- Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture);
- VS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
+ while (!preview_done) {
+ OS::get_singleton()->delay_usec(10);
+ }
- ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>());
+ Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture);
+ VS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
- int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
- thumbnail_size *= EDSCALE;
- img->convert(Image::FORMAT_RGBA8);
- img->resize(thumbnail_size, thumbnail_size);
- Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
- ptex->create_from_image(img, 0);
- return ptex;
+ ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>());
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+ img->convert(Image::FORMAT_RGBA8);
+ img->resize(thumbnail_size, thumbnail_size);
+ Ref<ImageTexture> ptex = Ref<ImageTexture>(memnew(ImageTexture));
+ ptex->create_from_image(img, 0);
+ return ptex;
+ }
+
+ return Ref<Texture>();
}
EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() {
@@ -790,13 +795,13 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from) {
VS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
- Rect3 aabb = mesh->get_aabb();
+ AABB aabb = mesh->get_aabb();
Vector3 ofs = aabb.position + aabb.size * 0.5;
aabb.position -= ofs;
Transform xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI * 0.125);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI * 0.125) * xform.basis;
- Rect3 rot_aabb = xform.xform(aabb);
+ AABB rot_aabb = xform.xform(aabb);
float m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
if (m == 0)
return Ref<Texture>();
diff --git a/editor/plugins/gi_probe_editor_plugin.cpp b/editor/plugins/gi_probe_editor_plugin.cpp
index 443cd2e41f..416b0edb20 100644
--- a/editor/plugins/gi_probe_editor_plugin.cpp
+++ b/editor/plugins/gi_probe_editor_plugin.cpp
@@ -90,7 +90,7 @@ GIProbeEditorPlugin::GIProbeEditorPlugin(EditorNode *p_node) {
editor = p_node;
bake = memnew(Button);
- bake->set_icon(editor->get_gui_base()->get_icon("BakedLight", "EditorIcons"));
+ bake->set_icon(editor->get_gui_base()->get_icon("Bake", "EditorIcons"));
bake->set_text(TTR("Bake GI Probe"));
bake->hide();
bake->connect("pressed", this, "_bake");
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp
index 485657d2c9..3febc99239 100644
--- a/editor/plugins/light_occluder_2d_editor_plugin.cpp
+++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -318,7 +318,7 @@ bool LightOccluder2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return false;
}
-void LightOccluder2DEditor::forward_draw_over_canvas(Control *p_canvas) {
+void LightOccluder2DEditor::forward_draw_over_viewport(Control *p_overlay) {
if (!node || !node->get_occluder_polygon().is_valid())
return;
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.h b/editor/plugins/light_occluder_2d_editor_plugin.h
index 068832d8ed..dc3ff74052 100644
--- a/editor/plugins/light_occluder_2d_editor_plugin.h
+++ b/editor/plugins/light_occluder_2d_editor_plugin.h
@@ -82,7 +82,7 @@ protected:
public:
Vector2 snap_point(const Vector2 &p_point) const;
- void forward_draw_over_canvas(Control *p_canvas);
+ void forward_draw_over_viewport(Control *p_overlay);
bool forward_gui_input(const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
LightOccluder2DEditor(EditorNode *p_editor);
@@ -97,7 +97,7 @@ class LightOccluder2DEditorPlugin : public EditorPlugin {
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return light_occluder_editor->forward_gui_input(p_event); }
- virtual void forward_draw_over_canvas(Control *p_canvas) { return light_occluder_editor->forward_draw_over_canvas(p_canvas); }
+ virtual void forward_draw_over_viewport(Control *p_overlay) { return light_occluder_editor->forward_draw_over_viewport(p_overlay); }
virtual String get_name() const { return "LightOccluder2D"; }
bool has_main_screen() const { return false; }
diff --git a/editor/plugins/line_2d_editor_plugin.cpp b/editor/plugins/line_2d_editor_plugin.cpp
index 51fa488b43..04d8519b2f 100644
--- a/editor/plugins/line_2d_editor_plugin.cpp
+++ b/editor/plugins/line_2d_editor_plugin.cpp
@@ -61,10 +61,10 @@ void Line2DEditor::_action_set_polygon(int p_idx, const Variant &p_previous, con
undo_redo->add_undo_method(node, "set_points", p_previous);
}
-Line2DEditor::Line2DEditor(EditorNode *p_editor)
- : AbstractPolygon2DEditor(p_editor) {
+Line2DEditor::Line2DEditor(EditorNode *p_editor) :
+ AbstractPolygon2DEditor(p_editor) {
}
-Line2DEditorPlugin::Line2DEditorPlugin(EditorNode *p_node)
- : AbstractPolygon2DEditorPlugin(p_node, memnew(Line2DEditor(p_node)), "Line2D") {
+Line2DEditorPlugin::Line2DEditorPlugin(EditorNode *p_node) :
+ AbstractPolygon2DEditorPlugin(p_node, memnew(Line2DEditor(p_node)), "Line2D") {
}
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index 74618aecc2..60e8858b2d 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -95,7 +95,7 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) {
rot_y = 0;
_update_rotation();
- Rect3 aabb = mesh->get_aabb();
+ AABB aabb = mesh->get_aabb();
print_line("aabb: " + aabb);
Vector3 ofs = aabb.position + aabb.size * 0.5;
float m = aabb.get_longest_axis_size();
diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp
index 84fc0cecf2..9d116349c0 100644
--- a/editor/plugins/mesh_instance_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_editor_plugin.cpp
@@ -195,7 +195,139 @@ void MeshInstanceEditor::_menu_option(int p_option) {
outline_dialog->popup_centered(Vector2(200, 90));
} break;
+ case MENU_OPTION_CREATE_UV2: {
+
+ Ref<ArrayMesh> mesh = node->get_mesh();
+ if (!mesh.is_valid()) {
+ err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ Error err = mesh->lightmap_unwrap(node->get_global_transform());
+ if (err != OK) {
+ err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ } break;
+ case MENU_OPTION_DEBUG_UV1: {
+ Ref<Mesh> mesh = node->get_mesh();
+ if (!mesh.is_valid()) {
+ err_dialog->set_text(TTR("No mesh to debug."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ _create_uv_lines(0);
+ } break;
+ case MENU_OPTION_DEBUG_UV2: {
+ Ref<Mesh> mesh = node->get_mesh();
+ if (!mesh.is_valid()) {
+ err_dialog->set_text(TTR("No mesh to debug."));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+ _create_uv_lines(1);
+ } break;
+ }
+}
+
+struct MeshInstanceEditorEdgeSort {
+
+ Vector2 a;
+ Vector2 b;
+
+ bool operator<(const MeshInstanceEditorEdgeSort &p_b) const {
+ if (a == p_b.a)
+ return b < p_b.b;
+ else
+ return a < p_b.a;
+ }
+
+ MeshInstanceEditorEdgeSort() {}
+ MeshInstanceEditorEdgeSort(const Vector2 &p_a, const Vector2 &p_b) {
+ if (p_a < p_b) {
+ a = p_a;
+ b = p_b;
+ } else {
+ b = p_a;
+ a = p_b;
+ }
+ }
+};
+
+void MeshInstanceEditor::_create_uv_lines(int p_layer) {
+
+ Ref<Mesh> mesh = node->get_mesh();
+ ERR_FAIL_COND(!mesh.is_valid());
+
+ Set<MeshInstanceEditorEdgeSort> edges;
+ uv_lines.clear();
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
+ continue;
+ Array a = mesh->surface_get_arrays(i);
+
+ PoolVector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
+ if (uv.size() == 0) {
+ err_dialog->set_text(TTR("Model has no UV in this layer"));
+ err_dialog->popup_centered_minsize();
+ return;
+ }
+
+ PoolVector<Vector2>::Read r = uv.read();
+
+ PoolVector<int> indices = a[Mesh::ARRAY_INDEX];
+ PoolVector<int>::Read ri;
+
+ int ic;
+ bool use_indices;
+
+ if (indices.size()) {
+ ic = indices.size();
+ ri = indices.read();
+ use_indices = true;
+ } else {
+ ic = uv.size();
+ use_indices = false;
+ }
+
+ for (int j = 0; j < ic; j += 3) {
+
+ for (int k = 0; k < 3; k++) {
+
+ MeshInstanceEditorEdgeSort edge;
+ if (use_indices) {
+ edge.a = r[ri[j + k]];
+ edge.b = r[ri[j + ((k + 1) % 3)]];
+ } else {
+ edge.a = r[j + k];
+ edge.b = r[j + ((k + 1) % 3)];
+ }
+
+ if (edges.has(edge))
+ continue;
+
+ uv_lines.push_back(edge.a);
+ uv_lines.push_back(edge.b);
+ edges.insert(edge);
+ }
+ }
}
+
+ debug_uv_dialog->popup_centered_minsize();
+}
+
+void MeshInstanceEditor::_debug_uv_draw() {
+
+ if (uv_lines.size() == 0)
+ return;
+
+ debug_uv->set_clip_contents(true);
+ debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), Color(0.2, 0.2, 0.0));
+ debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
+ debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7));
}
void MeshInstanceEditor::_create_outline_mesh() {
@@ -244,6 +376,7 @@ void MeshInstanceEditor::_bind_methods() {
ClassDB::bind_method("_menu_option", &MeshInstanceEditor::_menu_option);
ClassDB::bind_method("_create_outline_mesh", &MeshInstanceEditor::_create_outline_mesh);
+ ClassDB::bind_method("_debug_uv_draw", &MeshInstanceEditor::_debug_uv_draw);
}
MeshInstanceEditor::MeshInstanceEditor() {
@@ -263,6 +396,10 @@ MeshInstanceEditor::MeshInstanceEditor() {
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Outline Mesh.."), MENU_OPTION_CREATE_OUTLINE_MESH);
+ options->get_popup()->add_separator();
+ options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
+ options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
+ options->get_popup()->add_item(TTR("Unwrap UV2 for Lightmap/AO"), MENU_OPTION_CREATE_UV2);
options->get_popup()->connect("id_pressed", this, "_menu_option");
@@ -286,6 +423,14 @@ MeshInstanceEditor::MeshInstanceEditor() {
err_dialog = memnew(AcceptDialog);
add_child(err_dialog);
+
+ debug_uv_dialog = memnew(AcceptDialog);
+ debug_uv_dialog->set_title("UV Channel Debug");
+ add_child(debug_uv_dialog);
+ debug_uv = memnew(Control);
+ debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE);
+ debug_uv->connect("draw", this, "_debug_uv_draw");
+ debug_uv_dialog->add_child(debug_uv);
}
void MeshInstanceEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/mesh_instance_editor_plugin.h b/editor/plugins/mesh_instance_editor_plugin.h
index fa851458ce..32c779509a 100644
--- a/editor/plugins/mesh_instance_editor_plugin.h
+++ b/editor/plugins/mesh_instance_editor_plugin.h
@@ -35,9 +35,9 @@
#include "scene/3d/mesh_instance.h"
#include "scene/gui/spin_box.h"
-class MeshInstanceEditor : public Node {
+class MeshInstanceEditor : public Control {
- GDCLASS(MeshInstanceEditor, Node);
+ GDCLASS(MeshInstanceEditor, Control);
enum Menu {
@@ -47,6 +47,9 @@ class MeshInstanceEditor : public Node {
MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE,
MENU_OPTION_CREATE_NAVMESH,
MENU_OPTION_CREATE_OUTLINE_MESH,
+ MENU_OPTION_CREATE_UV2,
+ MENU_OPTION_DEBUG_UV1,
+ MENU_OPTION_DEBUG_UV2,
};
MeshInstance *node;
@@ -58,11 +61,18 @@ class MeshInstanceEditor : public Node {
AcceptDialog *err_dialog;
+ AcceptDialog *debug_uv_dialog;
+ Control *debug_uv;
+ Vector<Vector2> uv_lines;
+
void _menu_option(int p_option);
void _create_outline_mesh();
+ void _create_uv_lines(int p_layer);
friend class MeshInstanceEditorPlugin;
+ void _debug_uv_draw();
+
protected:
void _node_removed(Node *p_node);
static void _bind_methods();
diff --git a/editor/plugins/navigation_mesh_generator.cpp b/editor/plugins/navigation_mesh_generator.cpp
index 5d50e9c855..005a132e22 100644
--- a/editor/plugins/navigation_mesh_generator.cpp
+++ b/editor/plugins/navigation_mesh_generator.cpp
@@ -189,8 +189,8 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(Ref<NavigationMesh>
ERR_FAIL_COND(tri_areas.size() == 0);
- memset(tri_areas.ptr(), 0, ntris * sizeof(unsigned char));
- rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptr());
+ memset(tri_areas.ptrw(), 0, ntris * sizeof(unsigned char));
+ rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, verts, nverts, tris, ntris, tri_areas.ptrw());
ERR_FAIL_COND(!rcRasterizeTriangles(&ctx, verts, nverts, tris, tri_areas.ptr(), ntris, *hf, cfg.walkableClimb));
}
diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp
index 6560a8dac7..36c608310b 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.cpp
+++ b/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -120,10 +120,10 @@ void NavigationPolygonEditor::_create_resource() {
_menu_option(MODE_CREATE);
}
-NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor)
- : AbstractPolygon2DEditor(p_editor) {
+NavigationPolygonEditor::NavigationPolygonEditor(EditorNode *p_editor) :
+ AbstractPolygon2DEditor(p_editor) {
}
-NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node)
- : AbstractPolygon2DEditorPlugin(p_node, memnew(NavigationPolygonEditor(p_node)), "NavigationPolygonInstance") {
+NavigationPolygonEditorPlugin::NavigationPolygonEditorPlugin(EditorNode *p_node) :
+ AbstractPolygon2DEditorPlugin(p_node, memnew(NavigationPolygonEditor(p_node)), "NavigationPolygonInstance") {
}
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index 5eaa248224..ff8a9f93d6 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -77,11 +77,6 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) {
case MENU_CLEAR_EMISSION_MASK: {
emission_mask->popup_centered_minsize();
-
- /*undo_redo->create_action(TTR("Clear Emission Mask"));
- undo_redo->add_do_method(particles, "set_emission_points", PoolVector<Vector2>());
- undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
- undo_redo->commit_action();*/
} break;
}
}
@@ -309,14 +304,6 @@ void Particles2DEditorPlugin::_generate_emission_mask() {
} else {
pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS);
}
-
- /*undo_redo->create_action(TTR("Set Emission Mask"));
- undo_redo->add_do_method(particles, "set_emission_points", epoints);
- undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
- undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
- undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
- undo_redo->commit_action();
- */
}
void Particles2DEditorPlugin::_notification(int p_what) {
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index 10834b74ff..52eba099c6 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -40,11 +40,6 @@ void ParticlesEditor::_node_removed(Node *p_node) {
}
}
-void ParticlesEditor::_resource_seleted(const String &p_res) {
-
- //print_line("selected resource path: "+p_res);
-}
-
void ParticlesEditor::_node_selected(const NodePath &p_path) {
Node *sel = get_node(p_path);
@@ -84,23 +79,6 @@ void ParticlesEditor::_node_selected(const NodePath &p_path) {
emission_dialog->popup_centered(Size2(300, 130));
}
-/*
-
-void ParticlesEditor::_populate() {
-
- if(!node)
- return;
-
-
- if (node->get_particles().is_null())
- return;
-
- node->get_particles()->set_instance_count(populate_amount->get_text().to_int());
- node->populate_parent(populate_rotate_random->get_val(),populate_tilt_random->get_val(),populate_scale_random->get_text().to_double(),populate_scale->get_text().to_double());
-
-}
-*/
-
void ParticlesEditor::_notification(int p_notification) {
if (p_notification == NOTIFICATION_ENTER_TREE) {
@@ -132,13 +110,7 @@ void ParticlesEditor::_menu_option(int p_option) {
EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
return;
}
- /*
- Node *root = get_scene()->get_root_node();
- ERR_FAIL_COND(!root);
- EditorNode *en = Object::cast_to<EditorNode>(root);
- ERR_FAIL_COND(!en);
- Node * node = en->get_edited_scene();
-*/
+
emission_tree_dialog->popup_centered_ratio();
} break;
@@ -153,15 +125,15 @@ void ParticlesEditor::_generate_aabb() {
EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
- Rect3 rect;
+ AABB rect;
while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
ep.step("Generating..", int(running), true);
OS::get_singleton()->delay_usec(1000);
- Rect3 capture = node->capture_aabb();
- if (rect == Rect3())
+ AABB capture = node->capture_aabb();
+ if (rect == AABB())
rect = capture;
else
rect.merge_with(capture);
@@ -247,7 +219,7 @@ void ParticlesEditor::_generate_emission_points() {
PoolVector<Face3>::Read r = geometry.read();
- Rect3 aabb;
+ AABB aabb;
for (int i = 0; i < gcount; i++) {
@@ -365,20 +337,14 @@ void ParticlesEditor::_generate_emission_points() {
material->set_emission_point_count(point_count);
material->set_emission_point_texture(tex);
}
-
- //print_line("point count: "+itos(points.size()));
- //node->set_emission_points(points);
}
void ParticlesEditor::_bind_methods() {
ClassDB::bind_method("_menu_option", &ParticlesEditor::_menu_option);
- ClassDB::bind_method("_resource_seleted", &ParticlesEditor::_resource_seleted);
ClassDB::bind_method("_node_selected", &ParticlesEditor::_node_selected);
ClassDB::bind_method("_generate_emission_points", &ParticlesEditor::_generate_emission_points);
ClassDB::bind_method("_generate_aabb", &ParticlesEditor::_generate_aabb);
-
- //ClassDB::bind_method("_populate",&ParticlesEditor::_populate);
}
ParticlesEditor::ParticlesEditor() {
@@ -394,8 +360,6 @@ ParticlesEditor::ParticlesEditor() {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Emission Points From Mesh"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_MESH);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
- // options->get_popup()->add_item(TTR("Clear Emitter"), MENU_OPTION_CLEAR_EMISSION_VOLUME);
-
options->get_popup()->connect("id_pressed", this, "_menu_option");
emission_dialog = memnew(ConfirmationDialog);
@@ -420,7 +384,6 @@ ParticlesEditor::ParticlesEditor() {
emission_dialog->connect("confirmed", this, "_generate_emission_points");
err_dialog = memnew(ConfirmationDialog);
- //err_dialog->get_cancel()->hide();
add_child(err_dialog);
emission_file_dialog = memnew(EditorFileDialog);
@@ -454,9 +417,6 @@ ParticlesEditor::ParticlesEditor() {
add_child(generate_aabb);
generate_aabb->connect("confirmed", this, "_generate_aabb");
-
- //options->set_anchor(MARGIN_LEFT,Control::ANCHOR_END);
- //options->set_anchor(MARGIN_RIGHT,Control::ANCHOR_END);
}
void ParticlesEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/particles_editor_plugin.h b/editor/plugins/particles_editor_plugin.h
index 2c8ce88eb2..a65538c7fa 100644
--- a/editor/plugins/particles_editor_plugin.h
+++ b/editor/plugins/particles_editor_plugin.h
@@ -73,7 +73,6 @@ class ParticlesEditor : public Control {
void _generate_aabb();
void _generate_emission_points();
- void _resource_seleted(const String &p_res);
void _node_selected(const NodePath &p_path);
void _menu_option(int);
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 2174f08e23..5e811bfa11 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -269,7 +269,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return false;
}
-void Path2DEditor::forward_draw_over_canvas(Control *p_canvas) {
+void Path2DEditor::forward_draw_over_viewport(Control *p_overlay) {
if (!node)
return;
diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h
index 516e48c471..638d933797 100644
--- a/editor/plugins/path_2d_editor_plugin.h
+++ b/editor/plugins/path_2d_editor_plugin.h
@@ -94,7 +94,7 @@ protected:
public:
bool forward_gui_input(const Ref<InputEvent> &p_event);
- void forward_draw_over_canvas(Control *p_canvas);
+ void forward_draw_over_viewport(Control *p_overlay);
void edit(Node *p_path2d);
Path2DEditor(EditorNode *p_editor);
};
@@ -108,7 +108,7 @@ class Path2DEditorPlugin : public EditorPlugin {
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return path2d_editor->forward_gui_input(p_event); }
- virtual void forward_draw_over_canvas(Control *p_canvas) { return path2d_editor->forward_draw_over_canvas(p_canvas); }
+ virtual void forward_draw_over_viewport(Control *p_overlay) { return path2d_editor->forward_draw_over_viewport(p_overlay); }
virtual String get_name() const { return "Path2D"; }
bool has_main_screen() const { return false; }
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index a525983c75..25e734187d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -339,6 +339,19 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
}
}
+
+ Ref<InputEventMagnifyGesture> magnify_gesture = p_input;
+ if (magnify_gesture.is_valid()) {
+
+ uv_zoom->set_value(uv_zoom->get_value() * magnify_gesture->get_factor());
+ }
+
+ Ref<InputEventPanGesture> pan_gesture = p_input;
+ if (pan_gesture.is_valid()) {
+
+ uv_hscroll->set_value(uv_hscroll->get_value() + uv_hscroll->get_page() * pan_gesture->get_delta().x / 8);
+ uv_vscroll->set_value(uv_vscroll->get_value() + uv_vscroll->get_page() * pan_gesture->get_delta().y / 8);
+ }
}
void Polygon2DEditor::_uv_scroll_changed(float) {
@@ -446,8 +459,8 @@ Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
return p_target;
}
-Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor)
- : AbstractPolygon2DEditor(p_editor) {
+Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
+ AbstractPolygon2DEditor(p_editor) {
snap_step = Vector2(10, 10);
use_snap = false;
@@ -596,6 +609,6 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor)
uv_edit_draw->set_clip_contents(true);
}
-Polygon2DEditorPlugin::Polygon2DEditorPlugin(EditorNode *p_node)
- : AbstractPolygon2DEditorPlugin(p_node, memnew(Polygon2DEditor(p_node)), "Polygon2D") {
+Polygon2DEditorPlugin::Polygon2DEditorPlugin(EditorNode *p_node) :
+ AbstractPolygon2DEditorPlugin(p_node, memnew(Polygon2DEditor(p_node)), "Polygon2D") {
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 607ccaa4e7..591e6dac56 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -586,6 +586,38 @@ void ScriptEditor::_close_docs_tab() {
}
}
+void ScriptEditor::_copy_script_path() {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ Ref<Script> script = se->get_edited_script();
+ OS::get_singleton()->set_clipboard(script->get_path());
+}
+
+void ScriptEditor::_close_other_tabs() {
+
+ int child_count = tab_container->get_child_count();
+ int current_idx = tab_container->get_current_tab();
+ for (int i = child_count - 1; i >= 0; i--) {
+
+ if (i == current_idx) {
+ continue;
+ }
+
+ tab_container->set_current_tab(i);
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+
+ if (se) {
+
+ // Maybe there are unsaved changes
+ if (se->is_unsaved()) {
+ _ask_close_current_unsaved_tab(se);
+ continue;
+ }
+ }
+
+ _close_current_tab();
+ }
+}
+
void ScriptEditor::_close_all_tabs() {
int child_count = tab_container->get_child_count();
@@ -855,7 +887,7 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog_option = FILE_SAVE_THEME_AS;
file_dialog->clear_filters();
file_dialog->add_filter("*.tet");
- file_dialog->set_current_path(EditorSettings::get_singleton()->get_settings_path() + "/text_editor_themes/" + EditorSettings::get_singleton()->get("text_editor/theme/color_theme"));
+ file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
file_dialog->popup_centered_ratio();
file_dialog->set_title(TTR("Save Theme As.."));
} break;
@@ -1000,9 +1032,15 @@ void ScriptEditor::_menu_option(int p_option) {
_close_current_tab();
}
} break;
+ case FILE_COPY_PATH: {
+ _copy_script_path();
+ } break;
case CLOSE_DOCS: {
_close_docs_tab();
} break;
+ case CLOSE_OTHER_TABS: {
+ _close_other_tabs();
+ } break;
case CLOSE_ALL: {
_close_all_tabs();
} break;
@@ -1078,6 +1116,9 @@ void ScriptEditor::_menu_option(int p_option) {
case CLOSE_DOCS: {
_close_docs_tab();
} break;
+ case CLOSE_OTHER_TABS: {
+ _close_other_tabs();
+ } break;
case CLOSE_ALL: {
_close_all_tabs();
} break;
@@ -1119,6 +1160,7 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("script_add_function_request", this, "_add_callback");
editor->connect("resource_saved", this, "_res_saved_callback");
script_list->connect("item_selected", this, "_script_selected");
+
members_overview->connect("item_selected", this, "_members_overview_selected");
help_overview->connect("item_selected", this, "_help_overview_selected");
script_split->connect("dragged", this, "_script_split_dragged");
@@ -1287,12 +1329,12 @@ void ScriptEditor::_members_overview_selected(int p_idx) {
if (!se) {
return;
}
- Dictionary state;
- state["scroll_position"] = members_overview->get_item_metadata(p_idx);
+ // Go to the member's line and reset the cursor column. We can't just change scroll_position
+ // directly, since code might be folded.
+ se->goto_line(members_overview->get_item_metadata(p_idx));
+ Dictionary state = se->get_edit_state();
state["column"] = 0;
- state["row"] = members_overview->get_item_metadata(p_idx);
se->set_edit_state(state);
- se->ensure_focus();
}
void ScriptEditor::_help_overview_selected(int p_idx) {
@@ -1583,7 +1625,7 @@ void ScriptEditor::_update_script_names() {
}
}
- if (_sort_list_on_update) {
+ if (_sort_list_on_update && !sedata.empty()) {
sedata.sort();
// change actual order of tab_container so that the order can be rearranged by user
@@ -1803,6 +1845,11 @@ void ScriptEditor::apply_scripts() const {
}
}
+void ScriptEditor::open_script_create_dialog(const String &p_base_name, const String &p_base_path) {
+ _menu_option(FILE_NEW);
+ script_create_dialog->config(p_base_name, p_base_path);
+}
+
void ScriptEditor::_editor_play() {
debugger->start();
@@ -2139,7 +2186,10 @@ void ScriptEditor::_make_script_list_context_menu() {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS);
context_menu->add_separator();
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT);
Ref<Script> scr = se->get_edited_script();
@@ -2458,6 +2508,7 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_close_discard_current_tab", &ScriptEditor::_close_discard_current_tab);
ClassDB::bind_method("_close_docs_tab", &ScriptEditor::_close_docs_tab);
ClassDB::bind_method("_close_all_tabs", &ScriptEditor::_close_all_tabs);
+ ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
@@ -2471,6 +2522,7 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_help_search", &ScriptEditor::_help_search);
ClassDB::bind_method("_help_index", &ScriptEditor::_help_index);
ClassDB::bind_method("_save_history", &ScriptEditor::_save_history);
+ ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
ClassDB::bind_method("_breaked", &ScriptEditor::_breaked);
ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);
@@ -2501,6 +2553,7 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script);
ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts);
+ ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog);
ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
@@ -2539,7 +2592,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_list->set_v_size_flags(SIZE_EXPAND_FILL);
script_split->set_split_offset(140);
//list_split->set_split_offset(500);
-
_sort_list_on_update = true;
script_list->connect("gui_input", this, "_script_list_gui_input");
script_list->set_allow_rmb_select(true);
@@ -2591,6 +2643,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Prev"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
@@ -2604,6 +2657,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_all", TTR("Close All")), CLOSE_ALL);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_other_tabs", TTR("Close Other Tabs")), CLOSE_OTHER_TABS);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/run_file", TTR("Run"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X), FILE_RUN);
file_menu->get_popup()->add_separator();
@@ -2861,7 +2915,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
EDITOR_DEF("text_editor/open_scripts/script_temperature_enabled", true);
EDITOR_DEF("text_editor/open_scripts/highlight_current_script", true);
EDITOR_DEF("text_editor/open_scripts/script_temperature_history_size", 15);
- EDITOR_DEF("text_editor/open_scripts/current_script_background_color", Color(1, 1, 1, 0.5));
+ EDITOR_DEF("text_editor/open_scripts/current_script_background_color", Color(1, 1, 1, 0.3));
EDITOR_DEF("text_editor/open_scripts/group_help_pages", true);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/open_scripts/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path"));
EDITOR_DEF("text_editor/open_scripts/sort_scripts_by", 0);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index b8317f9e86..9d5c110dec 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -135,7 +135,9 @@ class ScriptEditor : public PanelContainer {
FILE_CLOSE,
CLOSE_DOCS,
CLOSE_ALL,
+ CLOSE_OTHER_TABS,
TOGGLE_SCRIPTS_PANEL,
+ FILE_COPY_PATH,
FILE_TOOL_RELOAD,
FILE_TOOL_RELOAD_SOFT,
DEBUG_NEXT,
@@ -251,8 +253,11 @@ class ScriptEditor : public PanelContainer {
void _close_current_tab();
void _close_discard_current_tab(const String &p_str);
void _close_docs_tab();
+ void _close_other_tabs();
void _close_all_tabs();
+ void _copy_script_path();
+
void _ask_close_current_unsaved_tab(ScriptEditorBase *current);
bool grab_focus_block;
@@ -355,6 +360,7 @@ public:
void ensure_focus_current();
void apply_scripts() const;
+ void open_script_create_dialog(const String &p_base_name, const String &p_base_path);
void ensure_select_current();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 6b945157e8..0610f55b3f 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -96,10 +96,10 @@ void ScriptTextEditor::_load_theme_settings() {
Color member_variable_color = EDITOR_DEF("text_editor/highlighting/member_variable_color", Color(0.9, 0.3, 0.3));
Color mark_color = EDITOR_DEF("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
Color breakpoint_color = EDITOR_DEF("text_editor/highlighting/breakpoint_color", Color(0.8, 0.8, 0.4, 0.2));
+ Color code_folding_color = EDITOR_DEF("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
Color search_result_color = EDITOR_DEF("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
Color search_result_border_color = EDITOR_DEF("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1));
Color symbol_color = EDITOR_DEF("text_editor/highlighting/symbol_color", Color::hex(0x005291ff));
-
Color keyword_color = EDITOR_DEF("text_editor/highlighting/keyword_color", Color(0.5, 0.0, 0.2));
Color basetype_color = EDITOR_DEF("text_editor/highlighting/base_type_color", Color(0.3, 0.3, 0.0));
Color type_color = EDITOR_DEF("text_editor/highlighting/engine_type_color", Color(0.0, 0.2, 0.4));
@@ -137,6 +137,7 @@ void ScriptTextEditor::_load_theme_settings() {
member_variable_color = tm->get_color("text_editor/theme/member_variable_color", "Editor");
mark_color = tm->get_color("text_editor/theme/mark_color", "Editor");
breakpoint_color = tm->get_color("text_editor/theme/breakpoint_color", "Editor");
+ code_folding_color = tm->get_color("text_editor/theme/code_folding_color", "Editor");
search_result_color = tm->get_color("text_editor/theme/search_result_color", "Editor");
search_result_border_color = tm->get_color("text_editor/theme/search_result_border_color", "Editor");
}
@@ -160,8 +161,9 @@ void ScriptTextEditor::_load_theme_settings() {
text_edit->add_color_override("number_color", number_color);
text_edit->add_color_override("function_color", function_color);
text_edit->add_color_override("member_variable_color", member_variable_color);
- text_edit->add_color_override("mark_color", mark_color);
text_edit->add_color_override("breakpoint_color", breakpoint_color);
+ text_edit->add_color_override("mark_color", mark_color);
+ text_edit->add_color_override("code_folding_color", code_folding_color);
text_edit->add_color_override("search_result_color", search_result_color);
text_edit->add_color_override("search_result_border_color", search_result_border_color);
text_edit->add_color_override("symbol_color", symbol_color);
@@ -201,7 +203,7 @@ void ScriptTextEditor::_set_theme_for_script() {
text_edit->add_keyword_color("Rect2", basetype_color);
text_edit->add_keyword_color("Transform2D", basetype_color);
text_edit->add_keyword_color("Vector3", basetype_color);
- text_edit->add_keyword_color("Rect3", basetype_color);
+ text_edit->add_keyword_color("AABB", basetype_color);
text_edit->add_keyword_color("Basis", basetype_color);
text_edit->add_keyword_color("Plane", basetype_color);
text_edit->add_keyword_color("Transform", basetype_color);
@@ -518,7 +520,9 @@ void ScriptTextEditor::tag_saved_version() {
}
void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
- code_editor->get_text_edit()->call_deferred("cursor_set_line", p_line);
+ TextEdit *tx = code_editor->get_text_edit();
+ tx->unfold_line(p_line);
+ tx->call_deferred("cursor_set_line", p_line);
}
void ScriptTextEditor::ensure_focus() {
@@ -533,10 +537,6 @@ void ScriptTextEditor::set_edit_state(const Variant &p_state) {
code_editor->get_text_edit()->cursor_set_line(state["row"]);
code_editor->get_text_edit()->set_v_scroll(state["scroll_position"]);
code_editor->get_text_edit()->grab_focus();
-
- //int scroll_pos;
- //int cursor_column;
- //int cursor_row;
}
String ScriptTextEditor::get_name() {
@@ -712,15 +712,6 @@ void ScriptTextEditor::_breakpoint_toggled(int p_row) {
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
}
-static void swap_lines(TextEdit *tx, int line1, int line2) {
- String tmp = tx->get_line(line1);
- String tmp2 = tx->get_line(line2);
- tx->set_line(line2, tmp);
- tx->set_line(line1, tmp2);
-
- tx->cursor_set_line(line2);
-}
-
void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
Node *base = get_tree()->get_edited_scene_root();
@@ -799,39 +790,41 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
void ScriptTextEditor::_edit_option(int p_op) {
+ TextEdit *tx = code_editor->get_text_edit();
+
switch (p_op) {
case EDIT_UNDO: {
- code_editor->get_text_edit()->undo();
- code_editor->get_text_edit()->call_deferred("grab_focus");
+
+ tx->undo();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_REDO: {
- code_editor->get_text_edit()->redo();
- code_editor->get_text_edit()->call_deferred("grab_focus");
+
+ tx->redo();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_CUT: {
- code_editor->get_text_edit()->cut();
- code_editor->get_text_edit()->call_deferred("grab_focus");
+ tx->cut();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_COPY: {
- code_editor->get_text_edit()->copy();
- code_editor->get_text_edit()->call_deferred("grab_focus");
+ tx->copy();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_PASTE: {
- code_editor->get_text_edit()->paste();
- code_editor->get_text_edit()->call_deferred("grab_focus");
+ tx->paste();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_SELECT_ALL: {
- code_editor->get_text_edit()->select_all();
- code_editor->get_text_edit()->call_deferred("grab_focus");
-
+ tx->select_all();
+ tx->call_deferred("grab_focus");
} break;
case EDIT_MOVE_LINE_UP: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = script;
if (scr.is_null())
return;
@@ -850,6 +843,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (line_id == 0 || next_id < 0)
return;
+ tx->unfold_line(line_id);
+ tx->unfold_line(next_id);
+
tx->swap_lines(line_id, next_id);
tx->cursor_set_line(next_id);
}
@@ -863,16 +859,17 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (line_id == 0 || next_id < 0)
return;
+ tx->unfold_line(line_id);
+ tx->unfold_line(next_id);
+
tx->swap_lines(line_id, next_id);
tx->cursor_set_line(next_id);
}
tx->end_complex_operation();
tx->update();
-
} break;
case EDIT_MOVE_LINE_DOWN: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
@@ -891,6 +888,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count())
return;
+ tx->unfold_line(line_id);
+ tx->unfold_line(next_id);
+
tx->swap_lines(line_id, next_id);
tx->cursor_set_line(next_id);
}
@@ -904,6 +904,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (line_id == tx->get_line_count() - 1 || next_id > tx->get_line_count())
return;
+ tx->unfold_line(line_id);
+ tx->unfold_line(next_id);
+
tx->swap_lines(line_id, next_id);
tx->cursor_set_line(next_id);
}
@@ -913,57 +916,22 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case EDIT_INDENT_LEFT: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
- tx->begin_complex_operation();
- if (tx->is_selection_active()) {
- tx->indent_selection_left();
- } else {
- int begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- // begins with tab
- if (line_text.begins_with("\t")) {
- line_text = line_text.substr(1, line_text.length());
- tx->set_line(begin, line_text);
- }
- // begins with 4 spaces
- else if (line_text.begins_with(" ")) {
- line_text = line_text.substr(4, line_text.length());
- tx->set_line(begin, line_text);
- }
- }
- tx->end_complex_operation();
- tx->update();
- //tx->deselect();
-
+ tx->indent_left();
} break;
case EDIT_INDENT_RIGHT: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
- tx->begin_complex_operation();
- if (tx->is_selection_active()) {
- tx->indent_selection_right();
- } else {
- int begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- line_text = '\t' + line_text;
- tx->set_line(begin, line_text);
- }
- tx->end_complex_operation();
- tx->update();
- //tx->deselect();
-
+ tx->indent_right();
} break;
case EDIT_DELETE_LINE: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
@@ -972,13 +940,12 @@ void ScriptTextEditor::_edit_option(int p_op) {
int line = tx->cursor_get_line();
tx->set_line(tx->cursor_get_line(), "");
tx->backspace_at_cursor();
+ tx->unfold_line(line);
tx->cursor_set_line(line);
tx->end_complex_operation();
-
} break;
case EDIT_CLONE_DOWN: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
@@ -997,6 +964,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
tx->begin_complex_operation();
for (int i = from_line; i <= to_line; i++) {
+ tx->unfold_line(i);
if (i >= tx->get_line_count() - 1) {
tx->set_line(i, tx->get_line(i) + "\n");
}
@@ -1012,15 +980,40 @@ void ScriptTextEditor::_edit_option(int p_op) {
tx->end_complex_operation();
tx->update();
+ } break;
+ case EDIT_TOGGLE_FOLD_LINE: {
+
+ tx->toggle_fold_line(tx->cursor_get_line());
+ tx->update();
+ } break;
+ case EDIT_FOLD_ALL_LINES: {
+
+ tx->fold_all_lines();
+ tx->update();
+ } break;
+ case EDIT_UNFOLD_ALL_LINES: {
+ tx->unhide_all_lines();
+ tx->update();
} break;
case EDIT_TOGGLE_COMMENT: {
- TextEdit *tx = code_editor->get_text_edit();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
+ String delimiter = "#";
+ List<String> comment_delimiters;
+ scr->get_language()->get_comment_delimiters(&comment_delimiters);
+
+ for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) {
+ String script_delimiter = E->get();
+ if (script_delimiter.find(" ") == -1) {
+ delimiter = script_delimiter;
+ break;
+ }
+ }
+
tx->begin_complex_operation();
if (tx->is_selection_active()) {
int begin = tx->get_selection_from_line();
@@ -1033,7 +1026,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
// Check if all lines in the selected block are commented
bool is_commented = true;
for (int i = begin; i <= end; i++) {
- if (!tx->get_line(i).begins_with("#")) {
+ if (!tx->get_line(i).begins_with(delimiter)) {
is_commented = false;
break;
}
@@ -1042,12 +1035,12 @@ void ScriptTextEditor::_edit_option(int p_op) {
String line_text = tx->get_line(i);
if (line_text.strip_edges().empty()) {
- line_text = "#";
+ line_text = delimiter;
} else {
if (is_commented) {
- line_text = line_text.substr(1, line_text.length());
+ line_text = line_text.substr(delimiter.length(), line_text.length());
} else {
- line_text = "#" + line_text;
+ line_text = delimiter + line_text;
}
}
tx->set_line(i, line_text);
@@ -1056,71 +1049,74 @@ void ScriptTextEditor::_edit_option(int p_op) {
int begin = tx->cursor_get_line();
String line_text = tx->get_line(begin);
- if (line_text.begins_with("#"))
- line_text = line_text.substr(1, line_text.length());
+ if (line_text.begins_with(delimiter))
+ line_text = line_text.substr(delimiter.length(), line_text.length());
else
- line_text = "#" + line_text;
+ line_text = delimiter + line_text;
tx->set_line(begin, line_text);
}
tx->end_complex_operation();
tx->update();
//tx->deselect();
-
} break;
case EDIT_COMPLETE: {
- code_editor->get_text_edit()->query_code_comple();
-
+ tx->query_code_comple();
} break;
case EDIT_AUTO_INDENT: {
- TextEdit *te = code_editor->get_text_edit();
- String text = te->get_text();
+ String text = tx->get_text();
Ref<Script> scr = get_edited_script();
if (scr.is_null())
return;
- te->begin_complex_operation();
+ tx->begin_complex_operation();
int begin, end;
- if (te->is_selection_active()) {
- begin = te->get_selection_from_line();
- end = te->get_selection_to_line();
+ if (tx->is_selection_active()) {
+ begin = tx->get_selection_from_line();
+ end = tx->get_selection_to_line();
// ignore if the cursor is not past the first column
- if (te->get_selection_to_column() == 0) {
+ if (tx->get_selection_to_column() == 0) {
end--;
}
} else {
begin = 0;
- end = te->get_line_count() - 1;
+ end = tx->get_line_count() - 1;
}
scr->get_language()->auto_indent_code(text, begin, end);
Vector<String> lines = text.split("\n");
for (int i = begin; i <= end; ++i) {
- te->set_line(i, lines[i]);
+ tx->set_line(i, lines[i]);
}
- te->end_complex_operation();
-
+ tx->end_complex_operation();
} break;
case EDIT_TRIM_TRAILING_WHITESAPCE: {
+
trim_trailing_whitespace();
} break;
case EDIT_CONVERT_INDENT_TO_SPACES: {
+
convert_indent_to_spaces();
} break;
case EDIT_CONVERT_INDENT_TO_TABS: {
+
convert_indent_to_tabs();
} break;
case EDIT_PICK_COLOR: {
+
color_panel->popup();
} break;
case EDIT_TO_UPPERCASE: {
+
_convert_case(UPPER);
} break;
case EDIT_TO_LOWERCASE: {
+
_convert_case(LOWER);
} break;
case EDIT_CAPITALIZE: {
+
_convert_case(CAPITALIZE);
} break;
case SEARCH_FIND: {
@@ -1145,41 +1141,47 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case SEARCH_GOTO_LINE: {
- goto_line_dialog->popup_find_line(code_editor->get_text_edit());
+ goto_line_dialog->popup_find_line(tx);
} break;
case DEBUG_TOGGLE_BREAKPOINT: {
- int line = code_editor->get_text_edit()->cursor_get_line();
- bool dobreak = !code_editor->get_text_edit()->is_line_set_as_breakpoint(line);
- code_editor->get_text_edit()->set_line_as_breakpoint(line, dobreak);
+
+ int line = tx->cursor_get_line();
+ bool dobreak = !tx->is_line_set_as_breakpoint(line);
+ tx->set_line_as_breakpoint(line, dobreak);
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak);
} break;
case DEBUG_REMOVE_ALL_BREAKPOINTS: {
+
List<int> bpoints;
- code_editor->get_text_edit()->get_breakpoints(&bpoints);
+ tx->get_breakpoints(&bpoints);
for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
int line = E->get();
- bool dobreak = !code_editor->get_text_edit()->is_line_set_as_breakpoint(line);
- code_editor->get_text_edit()->set_line_as_breakpoint(line, dobreak);
+ bool dobreak = !tx->is_line_set_as_breakpoint(line);
+ tx->set_line_as_breakpoint(line, dobreak);
ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(get_edited_script()->get_path(), line + 1, dobreak);
}
}
case DEBUG_GOTO_NEXT_BREAKPOINT: {
+
List<int> bpoints;
- code_editor->get_text_edit()->get_breakpoints(&bpoints);
+ tx->get_breakpoints(&bpoints);
if (bpoints.size() <= 0) {
return;
}
- int line = code_editor->get_text_edit()->cursor_get_line();
+ int line = tx->cursor_get_line();
+
// wrap around
if (line >= bpoints[bpoints.size() - 1]) {
- code_editor->get_text_edit()->cursor_set_line(bpoints[0]);
+ tx->unfold_line(bpoints[0]);
+ tx->cursor_set_line(bpoints[0]);
} else {
for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
int bline = E->get();
if (bline > line) {
- code_editor->get_text_edit()->cursor_set_line(bline);
+ tx->unfold_line(bline);
+ tx->cursor_set_line(bline);
return;
}
}
@@ -1187,21 +1189,24 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case DEBUG_GOTO_PREV_BREAKPOINT: {
+
List<int> bpoints;
- code_editor->get_text_edit()->get_breakpoints(&bpoints);
+ tx->get_breakpoints(&bpoints);
if (bpoints.size() <= 0) {
return;
}
- int line = code_editor->get_text_edit()->cursor_get_line();
+ int line = tx->cursor_get_line();
// wrap around
if (line <= bpoints[0]) {
- code_editor->get_text_edit()->cursor_set_line(bpoints[bpoints.size() - 1]);
+ tx->unfold_line(bpoints[bpoints.size() - 1]);
+ tx->cursor_set_line(bpoints[bpoints.size() - 1]);
} else {
for (List<int>::Element *E = bpoints.back(); E; E = E->prev()) {
int bline = E->get();
if (bline < line) {
- code_editor->get_text_edit()->cursor_set_line(bline);
+ tx->unfold_line(bline);
+ tx->cursor_set_line(bline);
return;
}
}
@@ -1210,9 +1215,10 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case HELP_CONTEXTUAL: {
- String text = code_editor->get_text_edit()->get_selection_text();
+
+ String text = tx->get_selection_text();
if (text == "")
- text = code_editor->get_text_edit()->get_word_under_cursor();
+ text = tx->get_word_under_cursor();
if (text != "") {
emit_signal("request_help_search", text);
}
@@ -1398,6 +1404,9 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
Vector2 mpos = mb->get_global_position() - tx->get_global_position();
bool have_selection = (tx->get_selection_text().length() > 0);
bool have_color = (tx->get_word_at_pos(mpos) == "Color");
+ int fold_state = 0;
+ bool can_fold = tx->can_fold(row);
+ bool is_folded = tx->is_folded(row);
if (have_color) {
String line = tx->get_line(row);
@@ -1428,7 +1437,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
have_color = false;
}
}
- _make_context_menu(have_selection, have_color);
+ _make_context_menu(have_selection, have_color, can_fold, is_folded);
}
}
}
@@ -1447,7 +1456,7 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
code_editor->get_text_edit()->set_line(color_line, new_line);
}
-void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color) {
+void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded) {
context_menu->clear();
if (p_selection) {
@@ -1460,13 +1469,19 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color) {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/select_all"), EDIT_SELECT_ALL);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/undo"), EDIT_UNDO);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/redo"), EDIT_REDO);
+ context_menu->add_separator();
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
if (p_selection) {
context_menu->add_separator();
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT);
- context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_uppercase"), EDIT_TO_UPPERCASE);
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_to_lowercase"), EDIT_TO_LOWERCASE);
}
+ if (p_can_fold || p_is_folded)
+ context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE);
+
if (p_color) {
context_menu->add_separator();
context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR);
@@ -1530,6 +1545,9 @@ ScriptTextEditor::ScriptTextEditor() {
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/clone_down"), EDIT_CLONE_DOWN);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES);
edit_menu->get_popup()->add_separator();
#ifdef OSX_ENABLED
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/complete_symbol"), EDIT_COMPLETE);
@@ -1607,6 +1625,9 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0);
ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K);
ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B);
+ ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F);
+ ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), 0);
+ ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0);
#ifdef OSX_ENABLED
ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE);
#else
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 83f3ea57c0..e3b81e7c3f 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -91,6 +91,9 @@ class ScriptTextEditor : public ScriptEditorBase {
EDIT_TO_UPPERCASE,
EDIT_TO_LOWERCASE,
EDIT_CAPITALIZE,
+ EDIT_TOGGLE_FOLD_LINE,
+ EDIT_FOLD_ALL_LINES,
+ EDIT_UNFOLD_ALL_LINES,
SEARCH_FIND,
SEARCH_FIND_NEXT,
SEARCH_FIND_PREV,
@@ -118,7 +121,7 @@ protected:
static void _bind_methods();
void _edit_option(int p_op);
- void _make_context_menu(bool p_selection, bool p_color);
+ void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
void _color_changed(const Color &p_color);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 49e4642049..3e00776dfd 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -81,6 +81,7 @@ void ShaderTextEditor::_load_theme_settings() {
Color member_variable_color = EDITOR_DEF("text_editor/highlighting/member_variable_color", Color(0.9, 0.3, 0.3));
Color mark_color = EDITOR_DEF("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
Color breakpoint_color = EDITOR_DEF("text_editor/highlighting/breakpoint_color", Color(0.8, 0.8, 0.4, 0.2));
+ Color code_folding_color = EDITOR_DEF("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
Color search_result_color = EDITOR_DEF("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
Color search_result_border_color = EDITOR_DEF("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1));
Color symbol_color = EDITOR_DEF("text_editor/highlighting/symbol_color", Color::hex(0x005291ff));
@@ -122,6 +123,7 @@ void ShaderTextEditor::_load_theme_settings() {
member_variable_color = tm->get_color("text_editor/theme/member_variable_color", "Editor");
mark_color = tm->get_color("text_editor/theme/mark_color", "Editor");
breakpoint_color = tm->get_color("text_editor/theme/breakpoint_color", "Editor");
+ code_folding_color = tm->get_color("text_editor/theme/code_folding_color", "Editor");
search_result_color = tm->get_color("text_editor/theme/search_result_color", "Editor");
search_result_border_color = tm->get_color("text_editor/theme/search_result_border_color", "Editor");
}
@@ -147,6 +149,7 @@ void ShaderTextEditor::_load_theme_settings() {
get_text_edit()->add_color_override("member_variable_color", member_variable_color);
get_text_edit()->add_color_override("mark_color", mark_color);
get_text_edit()->add_color_override("breakpoint_color", breakpoint_color);
+ get_text_edit()->add_color_override("code_folding_color", code_folding_color);
get_text_edit()->add_color_override("search_result_color", search_result_color);
get_text_edit()->add_color_override("search_result_border_color", search_result_border_color);
get_text_edit()->add_color_override("symbol_color", symbol_color);
@@ -158,7 +161,7 @@ void ShaderTextEditor::_load_theme_settings() {
for (const Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = ShaderTypes::get_singleton()->get_functions(VisualServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
- for (const Map<StringName, ShaderLanguage::DataType>::Element *F = E->get().built_ins.front(); F; F = F->next()) {
+ for (const Map<StringName, ShaderLanguage::BuiltInInfo>::Element *F = E->get().built_ins.front(); F; F = F->next()) {
keywords.push_back(F->key());
}
}
@@ -376,26 +379,7 @@ void ShaderEditor::_menu_option(int p_option) {
if (shader.is_null())
return;
- tx->begin_complex_operation();
- if (tx->is_selection_active()) {
- tx->indent_selection_left();
- } else {
- int begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- // begins with tab
- if (line_text.begins_with("\t")) {
- line_text = line_text.substr(1, line_text.length());
- tx->set_line(begin, line_text);
- }
- // begins with 4 spaces
- else if (line_text.begins_with(" ")) {
- line_text = line_text.substr(4, line_text.length());
- tx->set_line(begin, line_text);
- }
- }
- tx->end_complex_operation();
- tx->update();
- //tx->deselect();
+ tx->indent_left();
} break;
case EDIT_INDENT_RIGHT: {
@@ -404,18 +388,7 @@ void ShaderEditor::_menu_option(int p_option) {
if (shader.is_null())
return;
- tx->begin_complex_operation();
- if (tx->is_selection_active()) {
- tx->indent_selection_right();
- } else {
- int begin = tx->cursor_get_line();
- String line_text = tx->get_line(begin);
- line_text = '\t' + line_text;
- tx->set_line(begin, line_text);
- }
- tx->end_complex_operation();
- tx->update();
- //tx->deselect();
+ tx->indent_right();
} break;
case EDIT_DELETE_LINE: {
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index ca80908ab5..0f27310264 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -758,17 +758,49 @@ bool SpatialEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_hig
}
}
+ bool is_plane_scale = false;
+ // plane select
+ if (col_axis == -1) {
+ col_d = 1e20;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized();
+ Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized();
+
+ Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST);
+
+ Vector3 r;
+ Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+
+ if (plane.intersects_ray(ray_pos, ray, &r)) {
+
+ float dist = r.distance_to(grabber_pos);
+ if (dist < (gs * GIZMO_PLANE_SIZE)) {
+
+ float d = ray_pos.distance_to(r);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+
+ is_plane_scale = true;
+ }
+ }
+ }
+ }
+ }
+
if (col_axis != -1) {
if (p_highlight_only) {
- spatial_editor->select_gizmo_highlight_axis(col_axis + 9);
+ spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_scale ? 12 : 9));
} else {
//handle scale
_edit.mode = TRANSFORM_SCALE;
_compute_edit(Point2(p_screenpos.x, p_screenpos.y));
- _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis);
+ _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0));
}
return true;
}
@@ -1065,7 +1097,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (get_selected_count() == 0)
break; //bye
- //handle rotate
+ //handle scale
_edit.mode = TRANSFORM_SCALE;
_compute_edit(b->get_position());
break;
@@ -1255,6 +1287,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector3 motion_mask;
Plane plane;
+ bool plane_mv = false;
switch (_edit.plane) {
case TRANSFORM_VIEW:
@@ -1273,6 +1306,21 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
break;
+ case TRANSFORM_YZ:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane_mv = true;
+ break;
+ case TRANSFORM_XZ:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0);
+ plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane_mv = true;
+ break;
+ case TRANSFORM_XY:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
+ plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane_mv = true;
+ break;
}
Vector3 intersection;
@@ -1284,8 +1332,19 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
Vector3 motion = intersection - click;
- if (motion_mask != Vector3()) {
- motion = motion_mask.dot(motion) * motion_mask;
+ if (_edit.plane != TRANSFORM_VIEW) {
+
+ if (!plane_mv) {
+
+ motion = motion_mask.dot(motion) * motion_mask;
+
+ } else {
+
+ // Alternative planar scaling mode
+ if (_get_key_modifier(m) != KEY_SHIFT) {
+ motion = motion_mask.dot(motion) * motion_mask;
+ }
+ }
} else {
float center_click_dist = click.distance_to(_edit.center);
@@ -1326,6 +1385,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
continue;
}
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
Transform original = se->original;
Transform original_local = se->original_local;
Transform base = Transform(Basis(), _edit.center);
@@ -1379,7 +1442,6 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- motion_mask = Vector3(0, 0, 0);
plane = Plane(_edit.center, _get_camera_normal());
break;
case TRANSFORM_X_AXIS:
@@ -1417,7 +1479,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
Vector3 motion = intersection - click;
- if (motion_mask != Vector3()) {
+ if (_edit.plane != TRANSFORM_VIEW) {
if (!plane_mv) {
motion = motion_mask.dot(motion) * motion_mask;
}
@@ -1451,6 +1513,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
continue;
}
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
Transform original = se->original;
Transform t;
@@ -1547,6 +1613,10 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!se)
continue;
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
Transform t;
if (local_coords) {
@@ -1624,92 +1694,78 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (nav_mode) {
case NAVIGATION_PAN: {
+ _nav_pan(m, _get_warped_mouse_motion(m));
- real_t pan_speed = 1 / 150.0;
- int pan_speed_modifier = 10;
- if (nav_scheme == NAVIGATION_MAYA && m->get_shift())
- pan_speed *= pan_speed_modifier;
+ } break;
- Point2i relative = _get_warped_mouse_motion(m);
+ case NAVIGATION_ZOOM: {
+ _nav_zoom(m, m->get_relative());
- Transform camera_transform;
+ } break;
- camera_transform.translate(cursor.pos);
- camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot);
- camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
- Vector3 translation(-relative.x * pan_speed, relative.y * pan_speed, 0);
- translation *= cursor.distance / DISTANCE_DEFAULT;
- camera_transform.translate(translation);
- cursor.pos = camera_transform.origin;
+ case NAVIGATION_ORBIT: {
+ _nav_orbit(m, _get_warped_mouse_motion(m));
+
+ } break;
+
+ case NAVIGATION_LOOK: {
+ _nav_look(m, _get_warped_mouse_motion(m));
+
+ } break;
+
+ default: {}
+ }
+ }
+
+ Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
+ if (magnify_gesture.is_valid()) {
+
+ if (is_freelook_active())
+ scale_freelook_speed(magnify_gesture->get_factor());
+ else
+ scale_cursor_distance(1.0 / magnify_gesture->get_factor());
+ }
+
+ Ref<InputEventPanGesture> pan_gesture = p_event;
+ if (pan_gesture.is_valid()) {
+
+ NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
+ NavigationMode nav_mode = NAVIGATION_NONE;
+
+ if (nav_scheme == NAVIGATION_GODOT) {
+
+ int mod = _get_key_modifier(pan_gesture);
+
+ if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier"))
+ nav_mode = NAVIGATION_PAN;
+ else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier"))
+ nav_mode = NAVIGATION_ZOOM;
+ else if (mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier"))
+ nav_mode = NAVIGATION_ORBIT;
+
+ } else if (nav_scheme == NAVIGATION_MAYA) {
+ if (pan_gesture->get_alt())
+ nav_mode = NAVIGATION_PAN;
+ }
+
+ switch (nav_mode) {
+ case NAVIGATION_PAN: {
+ _nav_pan(m, pan_gesture->get_delta());
} break;
case NAVIGATION_ZOOM: {
- real_t zoom_speed = 1 / 80.0;
- int zoom_speed_modifier = 10;
- if (nav_scheme == NAVIGATION_MAYA && m->get_shift())
- zoom_speed *= zoom_speed_modifier;
-
- NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int();
- if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) {
- if (m->get_relative().x > 0)
- scale_cursor_distance(1 - m->get_relative().x * zoom_speed);
- else if (m->get_relative().x < 0)
- scale_cursor_distance(1.0 / (1 + m->get_relative().x * zoom_speed));
- } else {
- if (m->get_relative().y > 0)
- scale_cursor_distance(1 + m->get_relative().y * zoom_speed);
- else if (m->get_relative().y < 0)
- scale_cursor_distance(1.0 / (1 - m->get_relative().y * zoom_speed));
- }
+ _nav_zoom(m, pan_gesture->get_delta());
} break;
case NAVIGATION_ORBIT: {
- Point2i relative = _get_warped_mouse_motion(m);
-
- real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
- real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
-
- cursor.x_rot += relative.y * radians_per_pixel;
- cursor.y_rot += relative.x * radians_per_pixel;
- if (cursor.x_rot > Math_PI / 2.0)
- cursor.x_rot = Math_PI / 2.0;
- if (cursor.x_rot < -Math_PI / 2.0)
- cursor.x_rot = -Math_PI / 2.0;
- name = "";
- _update_name();
+ _nav_orbit(m, pan_gesture->get_delta());
+
} break;
case NAVIGATION_LOOK: {
- // Freelook only works properly in perspective.
- // It technically works too in ortho, but it's awful for a user due to fov being near zero
- if (!orthogonal) {
- Point2i relative = _get_warped_mouse_motion(m);
-
- real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
- real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
-
- // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
- Transform prev_camera_transform = to_camera_transform(cursor);
-
- cursor.x_rot += relative.y * radians_per_pixel;
- cursor.y_rot += relative.x * radians_per_pixel;
- if (cursor.x_rot > Math_PI / 2.0)
- cursor.x_rot = Math_PI / 2.0;
- if (cursor.x_rot < -Math_PI / 2.0)
- cursor.x_rot = -Math_PI / 2.0;
-
- // Look is like the opposite of Orbit: the focus point rotates around the camera
- Transform camera_transform = to_camera_transform(cursor);
- Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
- Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
- Vector3 diff = prev_pos - pos;
- cursor.pos += diff;
-
- name = "";
- _update_name();
- }
+ _nav_look(m, pan_gesture->get_delta());
} break;
@@ -1815,6 +1871,94 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
accept_event();
}
+void SpatialEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
+
+ const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
+
+ real_t pan_speed = 1 / 150.0;
+ int pan_speed_modifier = 10;
+ if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift())
+ pan_speed *= pan_speed_modifier;
+
+ Transform camera_transform;
+
+ camera_transform.translate(cursor.pos);
+ camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot);
+ camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
+ Vector3 translation(-p_relative.x * pan_speed, p_relative.y * pan_speed, 0);
+ translation *= cursor.distance / DISTANCE_DEFAULT;
+ camera_transform.translate(translation);
+ cursor.pos = camera_transform.origin;
+}
+
+void SpatialEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
+
+ const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
+
+ real_t zoom_speed = 1 / 80.0;
+ int zoom_speed_modifier = 10;
+ if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift())
+ zoom_speed *= zoom_speed_modifier;
+
+ NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int();
+ if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) {
+ if (p_relative.x > 0)
+ scale_cursor_distance(1 - p_relative.x * zoom_speed);
+ else if (p_relative.x < 0)
+ scale_cursor_distance(1.0 / (1 + p_relative.x * zoom_speed));
+ } else {
+ if (p_relative.y > 0)
+ scale_cursor_distance(1 + p_relative.y * zoom_speed);
+ else if (p_relative.y < 0)
+ scale_cursor_distance(1.0 / (1 - p_relative.y * zoom_speed));
+ }
+}
+
+void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
+
+ real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
+ real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+
+ cursor.x_rot += p_relative.y * radians_per_pixel;
+ cursor.y_rot += p_relative.x * radians_per_pixel;
+ if (cursor.x_rot > Math_PI / 2.0)
+ cursor.x_rot = Math_PI / 2.0;
+ if (cursor.x_rot < -Math_PI / 2.0)
+ cursor.x_rot = -Math_PI / 2.0;
+ name = "";
+ _update_name();
+}
+
+void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
+
+ // Freelook only works properly in perspective.
+ // It technically works too in ortho, but it's awful for a user due to fov being near zero
+ if (!orthogonal) {
+ real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
+ real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
+
+ // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
+ Transform prev_camera_transform = to_camera_transform(cursor);
+
+ cursor.x_rot += p_relative.y * radians_per_pixel;
+ cursor.y_rot += p_relative.x * radians_per_pixel;
+ if (cursor.x_rot > Math_PI / 2.0)
+ cursor.x_rot = Math_PI / 2.0;
+ if (cursor.x_rot < -Math_PI / 2.0)
+ cursor.x_rot = -Math_PI / 2.0;
+
+ // Look is like the opposite of Orbit: the focus point rotates around the camera
+ Transform camera_transform = to_camera_transform(cursor);
+ Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
+ Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
+ Vector3 diff = prev_pos - pos;
+ cursor.pos += diff;
+
+ name = "";
+ _update_name();
+ }
+}
+
void SpatialEditorViewport::set_freelook_active(bool active_now) {
if (!freelook_active && active_now) {
@@ -2011,7 +2155,7 @@ void SpatialEditorViewport::_notification(int p_what) {
if (se->aabb.has_no_surface()) {
- se->aabb = vi ? vi->get_aabb() : Rect3(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
+ se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
Transform t = sp->get_global_transform();
@@ -2099,6 +2243,29 @@ void SpatialEditorViewport::_notification(int p_what) {
}
}
+ // FPS Counter.
+ bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS));
+ if (show_fps != fps->is_visible()) {
+ if (show_fps)
+ fps->show();
+ else
+ fps->hide();
+ }
+
+ if (show_fps) {
+ String text;
+ const float temp_fps = Engine::get_singleton()->get_frames_per_second();
+ text += TTR("FPS") + ": " + itos(temp_fps) + " (" + String::num(1000.0f / temp_fps, 2) + " ms)";
+
+ if (fps_label->get_text() != text || surface->get_size() != prev_size) {
+ fps_label->set_text(text);
+ Size2 ms = fps->get_size();
+ Size2 size = surface->get_size();
+ size.y = ms.y + 20;
+ fps->set_position(size - ms - Vector2(20, 0) * EDSCALE);
+ }
+ }
+
prev_size = surface->get_size();
}
@@ -2109,6 +2276,7 @@ void SpatialEditorViewport::_notification(int p_what) {
surface->connect("mouse_entered", this, "_smouseenter");
surface->connect("mouse_exited", this, "_smouseexit");
info->add_style_override("panel", get_stylebox("panel", "Panel"));
+ fps->add_style_override("panel", get_stylebox("panel", "Panel"));
preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
_init_gizmo_instance(index);
}
@@ -2164,6 +2332,16 @@ static void draw_indicator_bar(Control &surface, real_t fill, Ref<Texture> icon)
void SpatialEditorViewport::_draw() {
+ EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over();
+ if (!over_plugin_list->empty()) {
+ over_plugin_list->forward_draw_over_viewport(surface);
+ }
+
+ EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over();
+ if (!force_over_plugin_list->empty()) {
+ force_over_plugin_list->forward_force_draw_over_viewport(surface);
+ }
+
if (surface->has_focus()) {
Size2 size = surface->get_size();
Rect2 r = Rect2(Point2(), size);
@@ -2431,6 +2609,13 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked(idx, !current);
} break;
+ case VIEW_FPS: {
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_FPS);
+ bool current = view_menu->get_popup()->is_item_checked(idx);
+ view_menu->get_popup()->set_item_checked(idx, !current);
+
+ } break;
case VIEW_DISPLAY_NORMAL: {
viewport->set_debug_draw(Viewport::DEBUG_DRAW_DISABLED);
@@ -2439,7 +2624,6 @@ void SpatialEditorViewport::_menu_option(int p_option) {
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
-
} break;
case VIEW_DISPLAY_WIREFRAME: {
@@ -2516,6 +2700,14 @@ void SpatialEditorViewport::_init_gizmo_instance(int p_idx) {
//VS::get_singleton()->instance_geometry_set_flag(scale_gizmo_instance[i],VS::INSTANCE_FLAG_DEPH_SCALE,true);
VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
VS::get_singleton()->instance_set_layer_mask(scale_gizmo_instance[i], layer);
+
+ scale_plane_gizmo_instance[i] = VS::get_singleton()->instance_create();
+ VS::get_singleton()->instance_set_base(scale_plane_gizmo_instance[i], spatial_editor->get_scale_plane_gizmo(i)->get_rid());
+ VS::get_singleton()->instance_set_scenario(scale_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
+ VS::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false);
+ //VS::get_singleton()->instance_geometry_set_flag(scale_plane_gizmo_instance[i],VS::INSTANCE_FLAG_DEPH_SCALE,true);
+ VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
+ VS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer);
}
}
@@ -2526,6 +2718,7 @@ void SpatialEditorViewport::_finish_gizmo_instances() {
VS::get_singleton()->free(move_plane_gizmo_instance[i]);
VS::get_singleton()->free(rotate_gizmo_instance[i]);
VS::get_singleton()->free(scale_gizmo_instance[i]);
+ VS::get_singleton()->free(scale_plane_gizmo_instance[i]);
}
}
void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
@@ -2622,6 +2815,8 @@ void SpatialEditorViewport::update_transform_gizmo_view() {
VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE));
VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
+ VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
+ VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
}
}
@@ -2653,6 +2848,20 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) {
camera->set_doppler_tracking(doppler ? Camera::DOPPLER_TRACKING_IDLE_STEP : Camera::DOPPLER_TRACKING_DISABLED);
view_menu->get_popup()->set_item_checked(idx, doppler);
}
+ if (p_state.has("gizmos")) {
+ bool gizmos = p_state["gizmos"];
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
+ if (view_menu->get_popup()->is_item_checked(idx) != gizmos)
+ _menu_option(VIEW_GIZMOS);
+ }
+ if (p_state.has("information")) {
+ bool information = p_state["information"];
+
+ int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION);
+ if (view_menu->get_popup()->is_item_checked(idx) != information)
+ _menu_option(VIEW_INFORMATION);
+ }
if (p_state.has("half_res")) {
bool half_res = p_state["half_res"];
@@ -2684,6 +2893,9 @@ Dictionary SpatialEditorViewport::get_state() const {
d["use_environment"] = camera->get_environment().is_valid();
d["use_orthogonal"] = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
d["listener"] = viewport->is_audio_listener();
+ d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER));
+ d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS));
+ d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
d["half_res"] = viewport_container->get_stretch_shrink() > 1;
if (previewing) {
d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing);
@@ -2756,7 +2968,7 @@ void SpatialEditorViewport::focus_selection() {
cursor.pos = center;
}
-void SpatialEditorViewport::assign_pending_data_pointers(Spatial *p_preview_node, Rect3 *p_preview_bounds, AcceptDialog *p_accept) {
+void SpatialEditorViewport::assign_pending_data_pointers(Spatial *p_preview_node, AABB *p_preview_bounds, AcceptDialog *p_accept) {
preview_node = p_preview_node;
preview_bounds = p_preview_bounds;
accept = p_accept;
@@ -2819,14 +3031,14 @@ Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const
return point + offset;
}
-Rect3 SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, const Rect3 p_bounds) {
- Rect3 bounds = p_bounds;
+AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, const AABB p_bounds) {
+ AABB bounds = p_bounds;
for (int i = 0; i < p_parent->get_child_count(); i++) {
Spatial *child = Object::cast_to<Spatial>(p_parent->get_child(i));
if (child) {
MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child);
if (mesh_instance) {
- Rect3 mesh_instance_bounds = mesh_instance->get_aabb();
+ AABB mesh_instance_bounds = mesh_instance->get_aabb();
mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin;
bounds.merge_with(mesh_instance_bounds);
}
@@ -2858,7 +3070,7 @@ void SpatialEditorViewport::_create_preview(const Vector<String> &files) const {
editor->get_scene_root()->add_child(preview_node);
}
}
- *preview_bounds = _calculate_spatial_bounds(preview_node, Rect3());
+ *preview_bounds = _calculate_spatial_bounds(preview_node, AABB());
}
void SpatialEditorViewport::_remove_preview() {
@@ -3149,6 +3361,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS);
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION);
+ view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_fps", TTR("View FPS")), VIEW_FPS);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_half_resolution", TTR("Half Resolution")), VIEW_HALF_RESOLUTION);
@@ -3175,6 +3388,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
preview_camera->set_toggle_mode(true);
preview_camera->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
+ preview_camera->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
+ preview_camera->set_h_grow_direction(GROW_DIRECTION_BEGIN);
preview_camera->set_text(TTR("preview"));
surface->add_child(preview_camera);
preview_camera->hide();
@@ -3191,6 +3406,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
info->add_child(info_label);
info->hide();
+ // FPS Counter.
+ fps = memnew(PanelContainer);
+ fps->set_self_modulate(Color(1, 1, 1, 0.4));
+ surface->add_child(fps);
+ fps_label = memnew(Label);
+ fps->add_child(fps_label);
+ fps->hide();
+
accept = NULL;
freelook_active = false;
@@ -3528,13 +3751,18 @@ void SpatialEditor::select_gizmo_highlight_axis(int p_axis) {
move_plane_gizmo[i]->surface_set_material(0, (i + 6) == p_axis ? gizmo_hl : plane_gizmo_color[i]);
rotate_gizmo[i]->surface_set_material(0, (i + 3) == p_axis ? gizmo_hl : gizmo_color[i]);
scale_gizmo[i]->surface_set_material(0, (i + 9) == p_axis ? gizmo_hl : gizmo_color[i]);
+ scale_plane_gizmo[i]->surface_set_material(0, (i + 12) == p_axis ? gizmo_hl : plane_gizmo_color[i]);
}
}
+int SpatialEditor::get_skeleton_visibility_state() const {
+ return view_menu->get_popup()->get_item_state(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON));
+}
+
void SpatialEditor::update_transform_gizmo() {
List<Node *> &selection = editor_selection->get_selected_node_list();
- Rect3 center;
+ AABB center;
bool first = true;
Basis gizmo_basis;
@@ -3575,6 +3803,21 @@ void SpatialEditor::update_transform_gizmo() {
}
}
+void _update_all_gizmos(Node *p_node) {
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ Spatial *spatial_node = Object::cast_to<Spatial>(p_node->get_child(i));
+ if (spatial_node) {
+ spatial_node->update_gizmo();
+ }
+
+ _update_all_gizmos(p_node->get_child(i));
+ }
+}
+
+void SpatialEditor::update_all_gizmos() {
+ _update_all_gizmos(SceneTree::get_singleton()->get_root());
+}
+
Object *SpatialEditor::_get_editor_data(Object *p_what) {
Spatial *sp = Object::cast_to<Spatial>(p_what);
@@ -3595,7 +3838,7 @@ Object *SpatialEditor::_get_editor_data(Object *p_what) {
void SpatialEditor::_generate_selection_box() {
- Rect3 aabb(Vector3(), Vector3(1, 1, 1));
+ AABB aabb(Vector3(), Vector3(1, 1, 1));
aabb.grow_by(aabb.get_longest_axis_size() / 20.0);
Ref<SurfaceTool> st = memnew(SurfaceTool);
@@ -3641,8 +3884,7 @@ Dictionary SpatialEditor::get_state() const {
d["rotate_snap"] = get_rotate_snap();
d["scale_snap"] = get_scale_snap();
- int local_coords_index = transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_LOCAL_COORDS);
- d["local_coords"] = transform_menu->get_popup()->is_item_checked(local_coords_index);
+ d["local_coords"] = tool_option_button[TOOL_OPT_LOCAL_COORDS]->is_pressed();
int vc = 0;
if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT)))
@@ -3680,8 +3922,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
if (d.has("snap_enabled")) {
snap_enabled = d["snap_enabled"];
- int snap_enabled_idx = transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_USE_SNAP);
- transform_menu->get_popup()->set_item_checked(snap_enabled_idx, snap_enabled);
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["snap_enabled"]);
}
if (d.has("translate_snap"))
@@ -3694,8 +3935,7 @@ void SpatialEditor::set_state(const Dictionary &p_state) {
snap_scale->set_text(d["scale_snap"]);
if (d.has("local_coords")) {
- int local_coords_idx = transform_menu->get_popup()->get_item_index(MENU_TRANSFORM_LOCAL_COORDS);
- transform_menu->get_popup()->set_item_checked(local_coords_idx, d["local_coords"]);
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]);
update_transform_gizmo();
}
@@ -3835,6 +4075,22 @@ void SpatialEditor::_xform_dialog_action() {
undo_redo->commit_action();
}
+void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
+
+ switch (p_option) {
+ case MENU_TOOL_LOCAL_COORDS: {
+
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(pressed);
+ update_transform_gizmo();
+ } break;
+
+ case MENU_TOOL_USE_SNAP: {
+ tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed);
+ snap_enabled = pressed;
+ } break;
+ }
+}
+
void SpatialEditor::_menu_item_pressed(int p_option) {
switch (p_option) {
@@ -3848,29 +4104,13 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
for (int i = 0; i < TOOL_MAX; i++)
tool_button[i]->set_pressed(i == p_option);
tool_mode = (ToolMode)p_option;
-
- //static const char *_mode[]={"Selection Mode.","Translation Mode.","Rotation Mode.","Scale Mode.","List Selection Mode."};
- //set_message(_mode[p_option],3);
update_transform_gizmo();
} break;
- case MENU_TRANSFORM_USE_SNAP: {
-
- bool is_checked = transform_menu->get_popup()->is_item_checked(transform_menu->get_popup()->get_item_index(p_option));
- snap_enabled = !is_checked;
- transform_menu->get_popup()->set_item_checked(transform_menu->get_popup()->get_item_index(p_option), snap_enabled);
- } break;
case MENU_TRANSFORM_CONFIGURE_SNAP: {
snap_dialog->popup_centered(Size2(200, 180));
} break;
- case MENU_TRANSFORM_LOCAL_COORDS: {
-
- bool is_checked = transform_menu->get_popup()->is_item_checked(transform_menu->get_popup()->get_item_index(p_option));
- transform_menu->get_popup()->set_item_checked(transform_menu->get_popup()->get_item_index(p_option), !is_checked);
- update_transform_gizmo();
-
- } break;
case MENU_TRANSFORM_DIALOG: {
for (int i = 0; i < 3; i++) {
@@ -3984,6 +4224,66 @@ void SpatialEditor::_menu_item_pressed(int p_option) {
settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50));
} break;
+ case MENU_LOCK_SELECTED: {
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+
+ Spatial *spatial = Object::cast_to<Spatial>(E->get());
+ if (!spatial || !spatial->is_visible_in_tree())
+ continue;
+
+ if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ spatial->set_meta("_edit_lock_", true);
+ emit_signal("item_lock_status_changed");
+ }
+
+ _refresh_menu_icons();
+ } break;
+ case MENU_UNLOCK_SELECTED: {
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+
+ Spatial *spatial = Object::cast_to<Spatial>(E->get());
+ if (!spatial || !spatial->is_visible_in_tree())
+ continue;
+
+ if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
+ continue;
+
+ spatial->set_meta("_edit_lock_", Variant());
+ emit_signal("item_lock_status_changed");
+ }
+
+ _refresh_menu_icons();
+ } break;
+ case MENU_VISIBILITY_SKELETON: {
+
+ const int idx = view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON);
+ view_menu->get_popup()->toggle_item_multistate(idx);
+
+ // Change icon
+ const int state = view_menu->get_popup()->get_item_state(idx);
+ switch (state) {
+ case 0:
+ view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden"));
+ break;
+ case 1:
+ view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible"));
+ break;
+ case 2:
+ view_menu->get_popup()->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray"));
+ break;
+ }
+
+ update_all_gizmos();
+
+ } break;
}
}
@@ -4101,6 +4401,7 @@ void SpatialEditor::_init_indicators() {
move_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
+ scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
Ref<SpatialMaterial> mat = memnew(SpatialMaterial);
mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
@@ -4296,6 +4597,49 @@ void SpatialEditor::_init_indicators() {
surftool->set_material(mat);
surftool->commit(scale_gizmo[i]);
}
+
+ // Plane Scale
+ {
+ Ref<SurfaceTool> surftool = memnew(SurfaceTool);
+ surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ Vector3 vec = ivec2 - ivec3;
+ Vector3 plane[4] = {
+ vec * GIZMO_PLANE_DST,
+ vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE,
+ vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE),
+ vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE
+ };
+
+ Basis ma(ivec, Math_PI / 2);
+
+ Vector3 points[4] = {
+ ma.xform(plane[0]),
+ ma.xform(plane[1]),
+ ma.xform(plane[2]),
+ ma.xform(plane[3]),
+ };
+ surftool->add_vertex(points[0]);
+ surftool->add_vertex(points[1]);
+ surftool->add_vertex(points[2]);
+
+ surftool->add_vertex(points[0]);
+ surftool->add_vertex(points[2]);
+ surftool->add_vertex(points[3]);
+
+ Ref<SpatialMaterial> plane_mat = memnew(SpatialMaterial);
+ plane_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ plane_mat->set_on_top_of_alpha();
+ plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
+ plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ Color col;
+ col[i] = 1.0;
+ col.a = gizmo_alph;
+ plane_mat->set_albedo(col);
+ plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides
+ surftool->set_material(plane_mat);
+ surftool->commit(scale_plane_gizmo[i]);
+ }
}
}
@@ -4323,6 +4667,28 @@ bool SpatialEditor::is_any_freelook_active() const {
return false;
}
+void SpatialEditor::_refresh_menu_icons() {
+
+ bool all_locked = true;
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ if (selection.empty()) {
+ all_locked = false;
+ } else {
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_lock_")) {
+ all_locked = false;
+ break;
+ }
+ }
+ }
+
+ tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked);
+ tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty());
+ tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked);
+}
+
void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack())
@@ -4349,6 +4715,19 @@ void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
else if (ED_IS_SHORTCUT("spatial_editor/tool_scale", p_event))
_menu_item_pressed(MENU_TOOL_SCALE);
+
+ else if (ED_IS_SHORTCUT("spatial_editor/local_coords", p_event))
+ if (are_local_coords_enabled()) {
+ _menu_item_toggled(false, MENU_TOOL_LOCAL_COORDS);
+ } else {
+ _menu_item_toggled(true, MENU_TOOL_LOCAL_COORDS);
+ }
+ else if (ED_IS_SHORTCUT("spatial_editor/snap", p_event))
+ if (is_snap_enabled()) {
+ _menu_item_toggled(false, MENU_TOOL_USE_SNAP);
+ } else {
+ _menu_item_toggled(true, MENU_TOOL_USE_SNAP);
+ }
}
}
}
@@ -4361,6 +4740,11 @@ void SpatialEditor::_notification(int p_what) {
tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons"));
tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons"));
+ tool_button[SpatialEditor::TOOL_LOCK_SELECTED]->set_icon(get_icon("Lock", "EditorIcons"));
+ tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons"));
+
+ tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
+ tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_icon("Panels1", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_icon("Panels2", "EditorIcons"));
@@ -4368,10 +4752,15 @@ void SpatialEditor::_notification(int p_what) {
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons"));
+ view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VISIBILITY_SKELETON), view_menu->get_popup()->get_icon("visibility_visible"));
_menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
+ _refresh_menu_icons();
+
get_tree()->connect("node_removed", this, "_node_removed");
+ EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", this, "_refresh_menu_icons");
+ editor_selection->connect("selection_changed", this, "_refresh_menu_icons");
}
if (p_what == NOTIFICATION_ENTER_TREE) {
@@ -4392,6 +4781,9 @@ void SpatialEditor::_notification(int p_what) {
tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons"));
+ tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
+ tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
+
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_icon("Panels1", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_icon("Panels2", "EditorIcons"));
view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_icon("Panels2Alt", "EditorIcons"));
@@ -4510,18 +4902,21 @@ void SpatialEditor::_bind_methods() {
ClassDB::bind_method("_unhandled_key_input", &SpatialEditor::_unhandled_key_input);
ClassDB::bind_method("_node_removed", &SpatialEditor::_node_removed);
ClassDB::bind_method("_menu_item_pressed", &SpatialEditor::_menu_item_pressed);
+ ClassDB::bind_method("_menu_item_toggled", &SpatialEditor::_menu_item_toggled);
ClassDB::bind_method("_xform_dialog_action", &SpatialEditor::_xform_dialog_action);
ClassDB::bind_method("_get_editor_data", &SpatialEditor::_get_editor_data);
ClassDB::bind_method("_request_gizmo", &SpatialEditor::_request_gizmo);
ClassDB::bind_method("_toggle_maximize_view", &SpatialEditor::_toggle_maximize_view);
+ ClassDB::bind_method("_refresh_menu_icons", &SpatialEditor::_refresh_menu_icons);
ADD_SIGNAL(MethodInfo("transform_key_request"));
+ ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
}
void SpatialEditor::clear() {
- settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 55.0));
- settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.1));
+ settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0));
+ settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05));
settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500.0));
for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
@@ -4571,6 +4966,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
Vector<Variant> button_binds;
button_binds.resize(1);
+ String sct;
tool_button[TOOL_MODE_SELECT] = memnew(ToolButton);
hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]);
@@ -4582,7 +4978,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_SELECT]->set_tooltip(TTR("Select Mode (Q)\n") + keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection"));
tool_button[TOOL_MODE_MOVE] = memnew(ToolButton);
-
hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]);
tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true);
tool_button[TOOL_MODE_MOVE]->set_flat(true);
@@ -4606,9 +5001,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds);
tool_button[TOOL_MODE_SCALE]->set_tooltip(TTR("Scale Mode (R)"));
- VSeparator *vs = memnew(VSeparator);
- hbc_menu->add_child(vs);
-
tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton);
hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]);
tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true);
@@ -4617,12 +5009,47 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds);
tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode)."));
+ tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton);
+ hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]);
+ button_binds[0] = MENU_LOCK_SELECTED;
+ tool_button[TOOL_LOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
+ tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved)."));
+
+ tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton);
+ hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
+ button_binds[0] = MENU_UNLOCK_SELECTED;
+ tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
+ tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved)."));
+
+ VSeparator *vs = memnew(VSeparator);
+ hbc_menu->add_child(vs);
+
+ tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton);
+ hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]);
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true);
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true);
+ button_binds[0] = MENU_TOOL_LOCAL_COORDS;
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", this, "_menu_item_toggled", button_binds);
+ ED_SHORTCUT("spatial_editor/local_coords", TTR("Local Coords"), KEY_T);
+ sct = ED_GET_SHORTCUT("spatial_editor/local_coords").ptr()->get_as_text();
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_tooltip(vformat(TTR("Local Space Mode (%s)"), sct));
+
+ tool_option_button[TOOL_OPT_USE_SNAP] = memnew(ToolButton);
+ hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]);
+ tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true);
+ tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true);
+ button_binds[0] = MENU_TOOL_USE_SNAP;
+ tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", this, "_menu_item_toggled", button_binds);
+ ED_SHORTCUT("spatial_editor/snap", TTR("Snap"), KEY_Y);
+ sct = ED_GET_SHORTCUT("spatial_editor/snap").ptr()->get_as_text();
+ tool_option_button[TOOL_OPT_USE_SNAP]->set_tooltip(vformat(TTR("Snap Mode (%s)"), sct));
+
vs = memnew(VSeparator);
hbc_menu->add_child(vs);
// Drag and drop support;
preview_node = memnew(Spatial);
- preview_bounds = Rect3();
+ preview_bounds = AABB();
ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7);
ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7);
@@ -4631,7 +5058,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3);
ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3);
ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal view"), KEY_KP_5);
- ED_SHORTCUT("spatial_editor/snap", TTR("Snap"), KEY_S);
ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F);
@@ -4653,12 +5079,8 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
hbc_menu->add_child(transform_menu);
p = transform_menu->get_popup();
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/use_snap", TTR("Use Snap")), MENU_TRANSFORM_USE_SNAP);
p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap..")), MENU_TRANSFORM_CONFIGURE_SNAP);
p->add_separator();
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Local Coords")), MENU_TRANSFORM_LOCAL_COORDS);
- //p->set_item_checked(p->get_item_count()-1,true);
- p->add_separator();
p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog..")), MENU_TRANSFORM_DIALOG);
p->connect("id_pressed", this, "_menu_item_pressed");
@@ -4686,6 +5108,9 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
p->add_separator();
p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings")), MENU_VIEW_CAMERA_SETTINGS);
+ p->add_separator();
+ p->add_multistate_item(TTR("Skeleton Gizmo visibility"), 3, 1, MENU_VISIBILITY_SKELETON);
+
p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true);
p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true);
@@ -4748,14 +5173,14 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
settings_fov->set_max(MAX_FOV);
settings_fov->set_min(MIN_FOV);
settings_fov->set_step(0.01);
- settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 55.0));
+ settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0));
settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"), settings_fov);
settings_znear = memnew(SpinBox);
settings_znear->set_max(MAX_Z);
settings_znear->set_min(MIN_Z);
settings_znear->set_step(0.01);
- settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.1));
+ settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05));
settings_vbc->add_margin_child(TTR("View Z-Near:"), settings_znear);
settings_zfar = memnew(SpinBox);
diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h
index c2b698068f..86b814ab8a 100644
--- a/editor/plugins/spatial_editor_plugin.h
+++ b/editor/plugins/spatial_editor_plugin.h
@@ -88,10 +88,11 @@ class SpatialEditorViewport : public Control {
VIEW_AUDIO_DOPPLER,
VIEW_GIZMOS,
VIEW_INFORMATION,
+ VIEW_FPS,
VIEW_DISPLAY_NORMAL,
VIEW_DISPLAY_WIREFRAME,
VIEW_DISPLAY_OVERDRAW,
- VIEW_DISPLAY_SHADELESS,
+ VIEW_DISPLAY_SHADELESS
};
public:
@@ -108,7 +109,7 @@ private:
Size2 prev_size;
Spatial *preview_node;
- Rect3 *preview_bounds;
+ AABB *preview_bounds;
Vector<String> selected_files;
AcceptDialog *accept;
@@ -138,6 +139,9 @@ private:
PanelContainer *info;
Label *info_label;
+ PanelContainer *fps;
+ Label *fps_label;
+
struct _RayResult {
Spatial *item;
@@ -166,6 +170,11 @@ private:
void _select_region();
bool _gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false);
+ void _nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
+ void _nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
+ void _nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
+ void _nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);
+
float get_znear() const;
float get_zfar() const;
float get_fov() const;
@@ -255,7 +264,7 @@ private:
real_t zoom_indicator_delay;
- RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3];
+ RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[3], scale_gizmo_instance[3], scale_plane_gizmo_instance[3];
String last_message;
String message;
@@ -287,7 +296,7 @@ private:
Point2i _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const;
Vector3 _get_instance_position(const Point2 &p_pos) const;
- static Rect3 _calculate_spatial_bounds(const Spatial *p_parent, const Rect3 p_bounds);
+ static AABB _calculate_spatial_bounds(const Spatial *p_parent, const AABB p_bounds);
void _create_preview(const Vector<String> &files) const;
void _remove_preview();
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node);
@@ -302,6 +311,7 @@ protected:
static void _bind_methods();
public:
+ void update_surface() { surface->update(); }
void update_transform_gizmo_view();
void set_can_preview(Camera *p_preview);
@@ -314,7 +324,7 @@ public:
void assign_pending_data_pointers(
Spatial *p_preview_node,
- Rect3 *p_preview_bounds,
+ AABB *p_preview_bounds,
AcceptDialog *p_accept);
Viewport *get_viewport_node() { return viewport; }
@@ -327,7 +337,7 @@ class SpatialEditorSelectedItem : public Object {
GDCLASS(SpatialEditorSelectedItem, Object);
public:
- Rect3 aabb;
+ AABB aabb;
Transform original; // original location when moving
Transform original_local;
Transform last_xform; // last transform
@@ -380,6 +390,8 @@ class SpatialEditor : public VBoxContainer {
GDCLASS(SpatialEditor, VBoxContainer);
public:
+ static const unsigned int VIEWPORTS_COUNT = 4;
+
enum ToolMode {
TOOL_MODE_SELECT,
@@ -387,13 +399,21 @@ public:
TOOL_MODE_ROTATE,
TOOL_MODE_SCALE,
TOOL_MODE_LIST_SELECT,
+ TOOL_LOCK_SELECTED,
+ TOOL_UNLOCK_SELECTED,
TOOL_MAX
};
-private:
- static const unsigned int VIEWPORTS_COUNT = 4;
+ enum ToolOptions {
+
+ TOOL_OPT_LOCAL_COORDS,
+ TOOL_OPT_USE_SNAP,
+ TOOL_OPT_MAX
+ };
+
+private:
EditorNode *editor;
EditorSelection *editor_selection;
@@ -418,7 +438,7 @@ private:
bool grid_enable[3]; //should be always visible if true
bool grid_enabled;
- Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[3], scale_gizmo[3];
+ Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[3], scale_gizmo[3], scale_plane_gizmo[3];
Ref<SpatialMaterial> gizmo_color[3];
Ref<SpatialMaterial> plane_gizmo_color[3];
Ref<SpatialMaterial> gizmo_hl;
@@ -435,19 +455,8 @@ private:
// Scene drag and drop support
Spatial *preview_node;
- Rect3 preview_bounds;
-
- /*
- struct Selected {
- AABB aabb;
- Transform original; // original location when moving
- Transform last_xform; // last transform
- Spatial *sp;
- RID poly_instance;
- };
+ AABB preview_bounds;
- Map<uint32_t,Selected> selected;
-*/
struct Gizmo {
bool visible;
@@ -462,9 +471,9 @@ private:
MENU_TOOL_ROTATE,
MENU_TOOL_SCALE,
MENU_TOOL_LIST_SELECT,
- MENU_TRANSFORM_USE_SNAP,
+ MENU_TOOL_LOCAL_COORDS,
+ MENU_TOOL_USE_SNAP,
MENU_TRANSFORM_CONFIGURE_SNAP,
- MENU_TRANSFORM_LOCAL_COORDS,
MENU_TRANSFORM_DIALOG,
MENU_VIEW_USE_1_VIEWPORT,
MENU_VIEW_USE_2_VIEWPORTS,
@@ -475,14 +484,20 @@ private:
MENU_VIEW_ORIGIN,
MENU_VIEW_GRID,
MENU_VIEW_CAMERA_SETTINGS,
-
+ MENU_LOCK_SELECTED,
+ MENU_UNLOCK_SELECTED,
+ MENU_VISIBILITY_SKELETON
};
Button *tool_button[TOOL_MAX];
+ Button *tool_option_button[TOOL_OPT_MAX];
MenuButton *transform_menu;
MenuButton *view_menu;
+ ToolButton *lock_button;
+ ToolButton *unlock_button;
+
AcceptDialog *accept;
ConfirmationDialog *snap_dialog;
@@ -507,11 +522,10 @@ private:
void _xform_dialog_action();
void _menu_item_pressed(int p_option);
+ void _menu_item_toggled(bool pressed, int p_option);
HBoxContainer *hbc_menu;
- //
- //
void _generate_selection_box();
UndoRedo *undo_redo;
@@ -539,6 +553,8 @@ private:
bool is_any_freelook_active() const;
+ void _refresh_menu_icons();
+
protected:
void _notification(int p_what);
//void _gui_input(InputEvent p_event);
@@ -560,19 +576,22 @@ public:
bool is_gizmo_visible() const { return gizmo.visible; }
ToolMode get_tool_mode() const { return tool_mode; }
+ bool are_local_coords_enabled() const { return tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
bool is_snap_enabled() const { return snap_enabled; }
float get_translate_snap() const { return snap_translate->get_text().to_double(); }
float get_rotate_snap() const { return snap_rotate->get_text().to_double(); }
float get_scale_snap() const { return snap_scale->get_text().to_double(); }
- bool are_local_coords_enabled() const { return transform_menu->get_popup()->is_item_checked(transform_menu->get_popup()->get_item_index(SpatialEditor::MENU_TRANSFORM_LOCAL_COORDS)); }
-
Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; }
Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; }
Ref<ArrayMesh> get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; }
Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; }
+ Ref<ArrayMesh> get_scale_plane_gizmo(int idx) const { return scale_plane_gizmo[idx]; }
+
+ int get_skeleton_visibility_state() const;
void update_transform_gizmo();
+ void update_all_gizmos();
void select_gizmo_highlight_axis(int p_axis);
void set_custom_camera(Node *p_camera) { custom_camera = p_camera; }
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index b3bb103577..175655119f 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -394,6 +394,7 @@ void SpriteFramesEditor::_animation_add() {
edited_anim = name;
undo_redo->commit_action();
+ animations->grab_focus();
}
void SpriteFramesEditor::_animation_remove() {
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index 0ee0eed3a2..40abc4026a 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -123,12 +123,14 @@ void TileMapEditor::_menu_option(int p_option) {
return;
undo_redo->create_action(TTR("Erase Selection"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false, true);
+ _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
}
}
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
selection_active = false;
@@ -171,7 +173,7 @@ void TileMapEditor::set_selected_tile(int p_tile) {
}
}
-void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h, bool p_flip_v, bool p_transpose, bool p_with_undo) {
+void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h, bool p_flip_v, bool p_transpose) {
ERR_FAIL_COND(!node);
@@ -184,14 +186,8 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h,
if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose)
return; //check that it's actually different
- if (p_with_undo) {
-
- undo_redo->add_do_method(node, "set_cellv", Point2(p_pos), p_value, p_flip_h, p_flip_v, p_transpose);
- undo_redo->add_undo_method(node, "set_cellv", Point2(p_pos), prev_val, prev_flip_h, prev_flip_v, prev_transpose);
- } else {
-
- node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose);
- }
+ node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose);
+ node->update_bitmask_area(Point2(p_pos));
}
void TileMapEditor::_text_entered(const String &p_text) {
@@ -306,6 +302,12 @@ void TileMapEditor::_update_palette() {
if (tex.is_valid()) {
Rect2 region = tileset->tile_get_region(entries[i].id);
+ if (tileset->tile_get_is_autotile(entries[i].id)) {
+ int spacing = tileset->autotile_get_spacing(entries[i].id);
+ region.size = tileset->autotile_get_size(entries[i].id);
+ region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id);
+ }
+
if (!region.has_no_area())
palette->set_item_icon_region(palette->get_item_count() - 1, region);
@@ -363,7 +365,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era
return PoolVector<Vector2>();
}
- Rect2i r = node->get_item_rect();
+ Rect2i r = node->_edit_get_rect();
r.position = r.position / node->get_cell_size();
r.size = r.size / node->get_cell_size();
@@ -395,6 +397,7 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era
}
PoolVector<Vector2> points;
+ Vector<Vector2> non_preview_cache;
int count = 0;
int limit = 0;
@@ -423,8 +426,10 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era
bucket_cache_visited[loc] = true;
bucket_cache.push_back(n);
} else {
- node->set_cellv(n, id, flip_h, flip_v, transpose);
+ if (non_preview_cache.find(n) >= 0)
+ continue;
points.push_back(n);
+ non_preview_cache.push_back(n);
}
bucket_queue.push_back(Point2i(n.x, n.y + 1));
@@ -453,9 +458,10 @@ void TileMapEditor::_fill_points(const PoolVector<Vector2> p_points, const Dicti
bool tr = p_op["transpose"];
for (int i = 0; i < len; i++) {
-
_set_cell(pr[i], id, xf, yf, tr);
+ node->make_bitmask_area_dirty(pr[i]);
}
+ node->update_dirty_bitmask();
}
void TileMapEditor::_erase_points(const PoolVector<Vector2> p_points) {
@@ -499,6 +505,11 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h
Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell);
Rect2 r = node->get_tileset()->tile_get_region(p_cell);
+ if (node->get_tileset()->tile_get_is_autotile(p_cell)) {
+ int spacing = node->get_tileset()->autotile_get_spacing(p_cell);
+ r.size = node->get_tileset()->autotile_get_size(p_cell);
+ r.position += (r.size + Vector2(spacing, spacing)) * node->get_tileset()->autotile_get_icon_coordinate(p_cell);
+ }
Size2 sc = p_xform.get_scale();
Rect2 rect = Rect2();
@@ -551,20 +562,19 @@ void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h
}
} else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) {
- rect.position += node->get_cell_size() / 2;
- Vector2 s = r.size;
+ Size2 cell_size = node->get_cell_size();
- Vector2 center = (s / 2) - tile_ofs;
+ rect.position += tile_ofs;
if (p_flip_h)
- rect.position.x -= s.x - center.x;
+ rect.position.x -= cell_size.x / 2;
else
- rect.position.x -= center.x;
+ rect.position.x += cell_size.x / 2;
if (p_flip_v)
- rect.position.y -= s.y - center.y;
+ rect.position.y -= cell_size.y / 2;
else
- rect.position.y -= center.y;
+ rect.position.y += cell_size.y / 2;
}
rect.position = p_xform.xform(rect.position);
@@ -717,10 +727,8 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_PAINTING;
- paint_undo.clear();
- paint_undo[over_tile] = _get_op_from_cell(over_tile);
-
- _set_cell(over_tile, id, flip_h, flip_v, transpose);
+ undo_redo->create_action(TTR("Paint TileMap"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
}
} else if (tool == TOOL_PICKING) {
@@ -741,15 +749,10 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
int id = get_selected_tile();
- if (id != TileMap::INVALID_CELL && paint_undo.size()) {
-
- undo_redo->create_action(TTR("Paint TileMap"));
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
+ if (id != TileMap::INVALID_CELL) {
- Point2 p = E->key();
- undo_redo->add_do_method(node, "set_cellv", p, id, flip_h, flip_v, transpose);
- undo_redo->add_undo_method(node, "set_cellv", p, E->get().idx, E->get().xf, E->get().yf, E->get().tr);
- }
+ _set_cell(over_tile, id, flip_h, flip_v, transpose);
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
paint_undo.clear();
@@ -761,10 +764,12 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (id != TileMap::INVALID_CELL) {
undo_redo->create_action(TTR("Line Draw"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
- _set_cell(E->key(), id, flip_h, flip_v, transpose, true);
+ _set_cell(E->key(), id, flip_h, flip_v, transpose);
}
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
paint_undo.clear();
@@ -778,12 +783,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (id != TileMap::INVALID_CELL) {
undo_redo->create_action(TTR("Rectangle Paint"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
- _set_cell(Point2i(j, i), id, flip_h, flip_v, transpose, true);
+ _set_cell(Point2i(j, i), id, flip_h, flip_v, transpose);
}
}
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
canvas_item_editor->update();
@@ -793,10 +800,12 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2 ofs = over_tile - rectangle.position;
undo_redo->create_action(TTR("Duplicate"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
- _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose, true);
+ _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
}
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
copydata.clear();
@@ -809,28 +818,23 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else if (tool == TOOL_BUCKET) {
- Dictionary pop;
- pop["id"] = node->get_cell(over_tile.x, over_tile.y);
- pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
- pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
- pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
-
PoolVector<Vector2> points = _bucket_fill(over_tile);
if (points.size() == 0)
return false;
+ undo_redo->create_action(TTR("Bucket Fill"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+
Dictionary op;
op["id"] = get_selected_tile();
op["flip_h"] = flip_h;
op["flip_v"] = flip_v;
op["transpose"] = transpose;
- undo_redo->create_action(TTR("Bucket Fill"));
-
- undo_redo->add_do_method(this, "_fill_points", points, op);
- undo_redo->add_undo_method(this, "_fill_points", points, pop);
+ _fill_points(points, op);
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
undo_redo->commit_action();
// We want to keep the bucket-tool active
@@ -872,6 +876,9 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2 local = node->world_to_map(xform_inv.xform(mb->get_position()));
+ undo_redo->create_action(TTR("Erase TileMap"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+
if (mb->get_shift()) {
if (mb->get_control())
@@ -885,7 +892,6 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
tool = TOOL_ERASING;
- paint_undo[local] = _get_op_from_cell(local);
_set_cell(local, TileMap::INVALID_CELL);
}
@@ -895,18 +901,8 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
} else {
if (tool == TOOL_ERASING || tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
- if (paint_undo.size()) {
- undo_redo->create_action(TTR("Erase TileMap"));
- for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
-
- Point2 p = E->key();
- undo_redo->add_do_method(node, "set_cellv", p, TileMap::INVALID_CELL, false, false, false);
- undo_redo->add_undo_method(node, "set_cellv", p, E->get().idx, E->get().xf, E->get().yf, E->get().tr);
- }
-
- undo_redo->commit_action();
- paint_undo.clear();
- }
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
+ undo_redo->commit_action();
if (tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
canvas_item_editor->update();
@@ -993,10 +989,6 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
Point2i pos = points[i];
- if (!paint_undo.has(pos)) {
- paint_undo[pos] = _get_op_from_cell(pos);
- }
-
_set_cell(pos, TileMap::INVALID_CELL);
}
@@ -1169,7 +1161,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return false;
}
-void TileMapEditor::forward_draw_over_canvas(Control *p_canvas) {
+void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
if (!node)
return;
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 73474a3f3d..b5f2618576 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -137,6 +137,8 @@ class TileMapEditor : public VBoxContainer {
bool flip_h;
bool flip_v;
bool transpose;
+ int auto_x;
+ int auto_y;
};
List<TileData> copydata;
@@ -165,7 +167,7 @@ class TileMapEditor : public VBoxContainer {
void _update_palette();
void _menu_option(int p_option);
- void _set_cell(const Point2i &p_pos, int p_value, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, bool p_with_undo = false);
+ void _set_cell(const Point2i &p_pos, int p_value, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false);
void _canvas_mouse_enter();
void _canvas_mouse_exit();
@@ -182,7 +184,7 @@ public:
HBoxContainer *get_toolbar() const { return toolbar; }
bool forward_gui_input(const Ref<InputEvent> &p_event);
- void forward_draw_over_canvas(Control *p_canvas);
+ void forward_draw_over_viewport(Control *p_overlay);
void edit(Node *p_tile_map);
@@ -198,7 +200,7 @@ class TileMapEditorPlugin : public EditorPlugin {
public:
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tile_map_editor->forward_gui_input(p_event); }
- virtual void forward_draw_over_canvas(Control *p_canvas) { tile_map_editor->forward_draw_over_canvas(p_canvas); }
+ virtual void forward_draw_over_viewport(Control *p_overlay) { tile_map_editor->forward_draw_over_viewport(p_overlay); }
virtual String get_name() const { return "TileMap"; }
bool has_main_screen() const { return false; }
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index f2f71ba6b1..ae726b69ef 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "tile_set_editor_plugin.h"
+#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/physics_body_2d.h"
#include "scene/2d/sprite.h"
@@ -243,7 +244,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
MenuButton *options = memnew(MenuButton);
panel->add_child(options);
options->set_position(Point2(1, 1));
- options->set_text("Theme");
+ options->set_text(TTR("Tile Set"));
options->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
options->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM);
options->get_popup()->add_separator();
@@ -271,6 +272,7 @@ void TileSetEditorPlugin::edit(Object *p_node) {
if (Object::cast_to<TileSet>(p_node)) {
tileset_editor->edit(Object::cast_to<TileSet>(p_node));
tileset_editor->show();
+ autotile_editor->edit(p_node);
} else
tileset_editor->hide();
}
@@ -282,19 +284,1177 @@ bool TileSetEditorPlugin::handles(Object *p_node) const {
void TileSetEditorPlugin::make_visible(bool p_visible) {
- if (p_visible)
+ if (p_visible) {
tileset_editor->show();
- else
+ autotile_button->show();
+ autotile_editor->side_panel->show();
+ if (autotile_button->is_pressed()) {
+ autotile_editor->show();
+ }
+ } else {
tileset_editor->hide();
+ autotile_editor->side_panel->hide();
+ autotile_editor->hide();
+ autotile_button->hide();
+ }
}
TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
tileset_editor = memnew(TileSetEditor(p_node));
- p_node->get_viewport()->add_child(tileset_editor);
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, tileset_editor);
tileset_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE);
tileset_editor->set_anchor(MARGIN_BOTTOM, Control::ANCHOR_BEGIN);
tileset_editor->set_end(Point2(0, 22));
tileset_editor->hide();
+
+ autotile_editor = memnew(AutotileEditor(p_node));
+ add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, autotile_editor->side_panel);
+ autotile_editor->side_panel->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ autotile_editor->side_panel->set_custom_minimum_size(Size2(200, 0));
+ autotile_editor->side_panel->hide();
+ autotile_button = p_node->add_bottom_panel_item("Autotiles", autotile_editor);
+ autotile_button->hide();
+}
+
+AutotileEditor::AutotileEditor(EditorNode *p_editor) {
+
+ editor = p_editor;
+
+ //Side Panel
+ side_panel = memnew(Control);
+ side_panel->set_name("Autotiles");
+
+ VSplitContainer *split = memnew(VSplitContainer);
+ side_panel->add_child(split);
+ split->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+
+ autotile_list = memnew(ItemList);
+ autotile_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ autotile_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ autotile_list->set_custom_minimum_size(Size2(02, 200));
+ autotile_list->connect("item_selected", this, "_on_autotile_selected");
+ split->add_child(autotile_list);
+
+ property_editor = memnew(PropertyEditor);
+ property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ property_editor->set_h_size_flags(SIZE_EXPAND_FILL);
+ split->add_child(property_editor);
+
+ helper = memnew(AutotileEditorHelper(this));
+ property_editor->call_deferred("edit", helper);
+
+ // Editor
+
+ dragging_point = -1;
+ creating_shape = false;
+
+ set_custom_minimum_size(Size2(0, 150));
+
+ VBoxContainer *main_vb = memnew(VBoxContainer);
+ add_child(main_vb);
+ main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+
+ HBoxContainer *tool_hb = memnew(HBoxContainer);
+ Ref<ButtonGroup> g(memnew(ButtonGroup));
+
+ String label[EDITMODE_MAX] = { "Icon", "Bitmask", "Collision", "Occlusion", "Navigation", "Priority" };
+
+ for (int i = 0; i < (int)EDITMODE_MAX; i++) {
+ tool_editmode[i] = memnew(Button);
+ tool_editmode[i]->set_text(label[i]);
+ tool_editmode[i]->set_toggle_mode(true);
+ tool_editmode[i]->set_button_group(g);
+ Vector<Variant> args;
+ args.push_back(i);
+ tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", args);
+ tool_hb->add_child(tool_editmode[i]);
+ }
+ tool_editmode[EDITMODE_ICON]->set_pressed(true);
+
+ main_vb->add_child(tool_hb);
+ main_vb->add_child(memnew(HSeparator));
+
+ toolbar = memnew(HBoxContainer);
+ for (int i = 0; i < (int)TOOLBAR_MAX; i++) {
+ tool_containers[i] = memnew(HBoxContainer);
+ toolbar->add_child(tool_containers[i]);
+ tool_containers[i]->hide();
+ }
+
+ Ref<ButtonGroup> tg(memnew(ButtonGroup));
+
+ tools[TOOL_SELECT] = memnew(ToolButton);
+ tool_containers[TOOLBAR_DUMMY]->add_child(tools[TOOL_SELECT]);
+ tools[TOOL_SELECT]->set_tooltip("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.");
+ tools[TOOL_SELECT]->set_toggle_mode(true);
+ tools[TOOL_SELECT]->set_button_group(tg);
+ tools[TOOL_SELECT]->set_pressed(true);
+ tool_containers[TOOLBAR_DUMMY]->show();
+
+ Vector<Variant> p;
+ tools[BITMASK_COPY] = memnew(ToolButton);
+ p.push_back((int)BITMASK_COPY);
+ tools[BITMASK_COPY]->connect("pressed", this, "_on_tool_clicked", p);
+ tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_COPY]);
+ tools[BITMASK_PASTE] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)BITMASK_PASTE);
+ tools[BITMASK_PASTE]->connect("pressed", this, "_on_tool_clicked", p);
+ tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_PASTE]);
+ tools[BITMASK_CLEAR] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)BITMASK_CLEAR);
+ tools[BITMASK_CLEAR]->connect("pressed", this, "_on_tool_clicked", p);
+ tool_containers[TOOLBAR_BITMASK]->add_child(tools[BITMASK_CLEAR]);
+
+ tools[SHAPE_NEW_POLYGON] = memnew(ToolButton);
+ tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_NEW_POLYGON]);
+ tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true);
+ tools[SHAPE_NEW_POLYGON]->set_button_group(tg);
+ tool_containers[TOOLBAR_SHAPE]->add_child(memnew(VSeparator));
+ tools[SHAPE_DELETE] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)SHAPE_DELETE);
+ tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", p);
+ tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_DELETE]);
+ //tools[SHAPE_CREATE_FROM_NOT_BITMASKED] = memnew(ToolButton);
+ //tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_CREATE_FROM_NOT_BITMASKED]);
+ tool_containers[TOOLBAR_SHAPE]->add_change_receptor(memnew(VSeparator));
+ tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton);
+ tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true);
+ tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true);
+ tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_KEEP_INSIDE_TILE]);
+ tools[SHAPE_SNAP_TO_BITMASK_GRID] = memnew(ToolButton);
+ tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_toggle_mode(true);
+ tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_pressed(true);
+ tool_containers[TOOLBAR_SHAPE]->add_child(tools[SHAPE_SNAP_TO_BITMASK_GRID]);
+
+ spin_priority = memnew(SpinBox);
+ spin_priority->set_min(1);
+ spin_priority->set_max(255);
+ spin_priority->set_step(1);
+ spin_priority->set_custom_minimum_size(Size2(100, 0));
+ spin_priority->connect("value_changed", this, "_on_priority_changed");
+ spin_priority->hide();
+ toolbar->add_child(spin_priority);
+
+ Control *separator = memnew(Control);
+ separator->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->add_child(separator);
+
+ tools[ZOOM_OUT] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)ZOOM_OUT);
+ tools[ZOOM_OUT]->connect("pressed", this, "_on_tool_clicked", p);
+ toolbar->add_child(tools[ZOOM_OUT]);
+ tools[ZOOM_1] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)ZOOM_1);
+ tools[ZOOM_1]->connect("pressed", this, "_on_tool_clicked", p);
+ toolbar->add_child(tools[ZOOM_1]);
+ tools[ZOOM_IN] = memnew(ToolButton);
+ p = Vector<Variant>();
+ p.push_back((int)ZOOM_IN);
+ tools[ZOOM_IN]->connect("pressed", this, "_on_tool_clicked", p);
+ toolbar->add_child(tools[ZOOM_IN]);
+
+ main_vb->add_child(toolbar);
+
+ ScrollContainer *scroll = memnew(ScrollContainer);
+ main_vb->add_child(scroll);
+ scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ workspace_container = memnew(Control);
+ scroll->add_child(workspace_container);
+
+ workspace = memnew(Control);
+ workspace->connect("draw", this, "_on_workspace_draw");
+ workspace->connect("gui_input", this, "_on_workspace_input");
+ workspace_container->add_child(workspace);
+
+ preview = memnew(Sprite);
+ workspace->add_child(preview);
+ preview->set_centered(false);
+ preview->set_draw_behind_parent(true);
+ preview->set_region(true);
+}
+
+void AutotileEditor::_bind_methods() {
+
+ ClassDB::bind_method("_on_autotile_selected", &AutotileEditor::_on_autotile_selected);
+ ClassDB::bind_method("_on_edit_mode_changed", &AutotileEditor::_on_edit_mode_changed);
+ ClassDB::bind_method("_on_workspace_draw", &AutotileEditor::_on_workspace_draw);
+ ClassDB::bind_method("_on_workspace_input", &AutotileEditor::_on_workspace_input);
+ ClassDB::bind_method("_on_tool_clicked", &AutotileEditor::_on_tool_clicked);
+ ClassDB::bind_method("_on_priority_changed", &AutotileEditor::_on_priority_changed);
+}
+
+void AutotileEditor::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
+ tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons"));
+ tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons"));
+ tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons"));
+ tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons"));
+ tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons"));
+ tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons"));
+ tools[SHAPE_SNAP_TO_BITMASK_GRID]->set_icon(get_icon("SnapGrid", "EditorIcons"));
+ tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons"));
+ tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons"));
+ tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons"));
+ }
+}
+
+void AutotileEditor::_on_autotile_selected(int p_index) {
+
+ if (get_current_tile() >= 0) {
+ current_item_index = p_index;
+ preview->set_texture(tile_set->tile_get_texture(get_current_tile()));
+ preview->set_region_rect(tile_set->tile_get_region(get_current_tile()));
+ workspace->set_custom_minimum_size(tile_set->tile_get_region(get_current_tile()).size);
+ } else {
+ current_item_index = -1;
+ preview->set_texture(NULL);
+ workspace->set_custom_minimum_size(Size2i());
+ }
+ helper->_change_notify("");
+ workspace->update();
+}
+
+void AutotileEditor::_on_edit_mode_changed(int p_edit_mode) {
+
+ edit_mode = (EditMode)p_edit_mode;
+ switch (edit_mode) {
+ case EDITMODE_BITMASK: {
+ tool_containers[TOOLBAR_DUMMY]->show();
+ tool_containers[TOOLBAR_BITMASK]->show();
+ tool_containers[TOOLBAR_SHAPE]->hide();
+ tools[TOOL_SELECT]->set_pressed(true);
+ tools[TOOL_SELECT]->set_tooltip("LMB: set bit on.\nRMB: set bit off.");
+ spin_priority->hide();
+ } break;
+ case EDITMODE_COLLISION:
+ case EDITMODE_NAVIGATION:
+ case EDITMODE_OCCLUSION: {
+ tool_containers[TOOLBAR_DUMMY]->show();
+ tool_containers[TOOLBAR_BITMASK]->hide();
+ tool_containers[TOOLBAR_SHAPE]->show();
+ tools[TOOL_SELECT]->set_tooltip("Select current edited sub-tile.");
+ spin_priority->hide();
+ } break;
+ default: {
+ tool_containers[TOOLBAR_DUMMY]->show();
+ tool_containers[TOOLBAR_BITMASK]->hide();
+ tool_containers[TOOLBAR_SHAPE]->hide();
+ if (edit_mode == EDITMODE_ICON) {
+ tools[TOOL_SELECT]->set_tooltip("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.");
+ spin_priority->hide();
+ } else {
+ tools[TOOL_SELECT]->set_tooltip("Select sub-tile to change it's priority.");
+ spin_priority->show();
+ }
+ } break;
+ }
+ workspace->update();
+}
+
+void AutotileEditor::_on_workspace_draw() {
+
+ if (get_current_tile() >= 0 && !tile_set.is_null()) {
+ int spacing = tile_set->autotile_get_spacing(get_current_tile());
+ Vector2 size = tile_set->autotile_get_size(get_current_tile());
+ Rect2i region = tile_set->tile_get_region(get_current_tile());
+ Color c(0.347214, 0.722656, 0.617063);
+
+ switch (edit_mode) {
+ case EDITMODE_ICON: {
+ Vector2 coord = tile_set->autotile_get_icon_coordinate(get_current_tile());
+ draw_highlight_tile(coord);
+ } break;
+ case EDITMODE_BITMASK: {
+ c = Color(1, 0, 0, 0.5);
+ for (float x = 0; x < region.size.x / (spacing + size.x); x++) {
+ for (float y = 0; y < region.size.y / (spacing + size.y); y++) {
+ Vector2 coord(x, y);
+ Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
+ uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
+ if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ if (mask & TileSet::BIND_TOPLEFT) {
+ workspace->draw_rect(Rect2(anchor, size / 2), c);
+ }
+ if (mask & TileSet::BIND_TOPRIGHT) {
+ workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c);
+ }
+ if (mask & TileSet::BIND_BOTTOMLEFT) {
+ workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c);
+ }
+ if (mask & TileSet::BIND_BOTTOMRIGHT) {
+ workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c);
+ }
+ } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ if (mask & TileSet::BIND_TOPLEFT) {
+ workspace->draw_rect(Rect2(anchor, size / 3), c);
+ }
+ if (mask & TileSet::BIND_TOP) {
+ workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c);
+ }
+ if (mask & TileSet::BIND_TOPRIGHT) {
+ workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c);
+ }
+ if (mask & TileSet::BIND_LEFT) {
+ workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c);
+ }
+ if (mask & TileSet::BIND_CENTER) {
+ workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c);
+ }
+ if (mask & TileSet::BIND_RIGHT) {
+ workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c);
+ }
+ if (mask & TileSet::BIND_BOTTOMLEFT) {
+ workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c);
+ }
+ if (mask & TileSet::BIND_BOTTOM) {
+ workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c);
+ }
+ if (mask & TileSet::BIND_BOTTOMRIGHT) {
+ workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c);
+ }
+ }
+ }
+ }
+ } break;
+ case EDITMODE_COLLISION:
+ case EDITMODE_OCCLUSION:
+ case EDITMODE_NAVIGATION: {
+ Vector2 coord = edited_shape_coord;
+ draw_highlight_tile(coord);
+ draw_polygon_shapes();
+ } break;
+ case EDITMODE_PRIORITY: {
+ spin_priority->set_value(tile_set->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
+ uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), edited_shape_coord);
+ Vector<Vector2> queue_others;
+ int total = 0;
+ for (Map<Vector2, uint16_t>::Element *E = tile_set->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
+ if (E->value() == mask) {
+ total += tile_set->autotile_get_subtile_priority(get_current_tile(), E->key());
+ if (E->key() != edited_shape_coord) {
+ queue_others.push_back(E->key());
+ }
+ }
+ }
+ spin_priority->set_suffix(" / " + String::num(total, 0));
+ draw_highlight_tile(edited_shape_coord, queue_others);
+ } break;
+ }
+
+ float j = -size.x; //make sure to draw at 0
+ while (j < region.size.x) {
+ j += size.x;
+ if (spacing <= 0) {
+ workspace->draw_line(Point2(j, 0), Point2(j, region.size.y), c);
+ } else {
+ workspace->draw_rect(Rect2(Point2(j, 0), Size2(spacing, region.size.y)), c);
+ }
+ j += spacing;
+ }
+ j = -size.y; //make sure to draw at 0
+ while (j < region.size.y) {
+ j += size.y;
+ if (spacing <= 0) {
+ workspace->draw_line(Point2(0, j), Point2(region.size.x, j), c);
+ } else {
+ workspace->draw_rect(Rect2(Point2(0, j), Size2(region.size.x, spacing)), c);
+ }
+ j += spacing;
+ }
+ }
+}
+
+#define MIN_DISTANCE_SQUARED 10
+void AutotileEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
+
+ if (get_current_tile() >= 0 && !tile_set.is_null()) {
+ Ref<InputEventMouseButton> mb = p_ie;
+ Ref<InputEventMouseMotion> mm = p_ie;
+
+ static bool dragging;
+ static bool erasing;
+
+ int spacing = tile_set->autotile_get_spacing(get_current_tile());
+ Vector2 size = tile_set->autotile_get_size(get_current_tile());
+ switch (edit_mode) {
+ case EDITMODE_ICON: {
+ if (mb.is_valid()) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
+ tile_set->autotile_set_icon_coordinate(get_current_tile(), coord);
+ Rect2 region = tile_set->tile_get_region(get_current_tile());
+ region.size = size;
+ coord.x *= (spacing + size.x);
+ coord.y *= (spacing + size.y);
+ region.position += coord;
+ autotile_list->set_item_icon_region(current_item_index, region);
+ workspace->update();
+ }
+ }
+ } break;
+ case EDITMODE_BITMASK: {
+ if (mb.is_valid()) {
+ if (mb->is_pressed()) {
+ if (dragging) {
+ return;
+ }
+ if (mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) {
+ dragging = true;
+ erasing = (mb->get_button_index() == BUTTON_RIGHT);
+ Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
+ Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
+ pos = mb->get_position() - pos;
+ uint16_t bit;
+ if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ if (pos.x < size.x / 2) {
+ if (pos.y < size.y / 2) {
+ bit = TileSet::BIND_TOPLEFT;
+ } else {
+ bit = TileSet::BIND_BOTTOMLEFT;
+ }
+ } else {
+ if (pos.y < size.y / 2) {
+ bit = TileSet::BIND_TOPRIGHT;
+ } else {
+ bit = TileSet::BIND_BOTTOMRIGHT;
+ }
+ }
+ } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ if (pos.x < size.x / 3) {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOPLEFT;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOMLEFT;
+ } else {
+ bit = TileSet::BIND_LEFT;
+ }
+ } else if (pos.x > (size.x / 3) * 2) {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOPRIGHT;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOMRIGHT;
+ } else {
+ bit = TileSet::BIND_RIGHT;
+ }
+ } else {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOP;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOM;
+ } else {
+ bit = TileSet::BIND_CENTER;
+ }
+ }
+ }
+ uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
+ if (erasing) {
+ mask &= ~bit;
+ } else {
+ mask |= bit;
+ }
+ tile_set->autotile_set_bitmask(get_current_tile(), coord, mask);
+ workspace->update();
+ }
+ } else {
+ if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) {
+ dragging = false;
+ erasing = false;
+ }
+ }
+ }
+ if (mm.is_valid()) {
+ if (dragging) {
+ Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y)));
+ Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
+ pos = mm->get_position() - pos;
+ uint16_t bit;
+ if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ if (pos.x < size.x / 2) {
+ if (pos.y < size.y / 2) {
+ bit = TileSet::BIND_TOPLEFT;
+ } else {
+ bit = TileSet::BIND_BOTTOMLEFT;
+ }
+ } else {
+ if (pos.y < size.y / 2) {
+ bit = TileSet::BIND_TOPRIGHT;
+ } else {
+ bit = TileSet::BIND_BOTTOMRIGHT;
+ }
+ }
+ } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ if (pos.x < size.x / 3) {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOPLEFT;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOMLEFT;
+ } else {
+ bit = TileSet::BIND_LEFT;
+ }
+ } else if (pos.x > (size.x / 3) * 2) {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOPRIGHT;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOMRIGHT;
+ } else {
+ bit = TileSet::BIND_RIGHT;
+ }
+ } else {
+ if (pos.y < size.y / 3) {
+ bit = TileSet::BIND_TOP;
+ } else if (pos.y > (size.y / 3) * 2) {
+ bit = TileSet::BIND_BOTTOM;
+ } else {
+ bit = TileSet::BIND_CENTER;
+ }
+ }
+ }
+ uint16_t mask = tile_set->autotile_get_bitmask(get_current_tile(), coord);
+ if (erasing) {
+ mask &= ~bit;
+ } else {
+ mask |= bit;
+ }
+ tile_set->autotile_set_bitmask(get_current_tile(), coord, mask);
+ workspace->update();
+ }
+ }
+ } break;
+ case EDITMODE_COLLISION:
+ case EDITMODE_OCCLUSION:
+ case EDITMODE_NAVIGATION:
+ case EDITMODE_PRIORITY: {
+ Vector2 shape_anchor = edited_shape_coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ if (tools[TOOL_SELECT]->is_pressed()) {
+ if (mb.is_valid()) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) {
+ for (int i = 0; i < current_shape.size(); i++) {
+ if ((current_shape[i] - mb->get_position()).length_squared() <= MIN_DISTANCE_SQUARED) {
+ dragging_point = i;
+ workspace->update();
+ return;
+ }
+ }
+ }
+ Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
+ if (edited_shape_coord != coord) {
+ edited_shape_coord = coord;
+ edited_occlusion_shape = tile_set->autotile_get_light_occluder(get_current_tile(), edited_shape_coord);
+ edited_navigation_shape = tile_set->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord);
+ shape_anchor = edited_shape_coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ if (edit_mode == EDITMODE_OCCLUSION) {
+ current_shape.resize(0);
+ if (edited_occlusion_shape.is_valid()) {
+ for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
+ current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor);
+ }
+ }
+ } else if (edit_mode == EDITMODE_NAVIGATION) {
+ current_shape.resize(0);
+ if (edited_navigation_shape.is_valid()) {
+ if (edited_navigation_shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
+ for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
+ current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor);
+ }
+ }
+ }
+ }
+ } else {
+ if (edit_mode == EDITMODE_COLLISION) {
+ Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile());
+ for (int i = 0; i < sd.size(); i++) {
+ if (sd[i].autotile_coord == coord) {
+ Ref<ConcavePolygonShape2D> shape = sd[i].shape;
+ if (shape.is_valid()) {
+ //FIXME: i need a way to know if the point is countained on the polygon instead of the rect
+ Rect2 bounding_rect;
+ PoolVector2Array polygon;
+ bounding_rect.position = shape->get_segments()[0];
+ for (int j = 0; j < shape->get_segments().size(); j += 2) {
+ polygon.push_back(shape->get_segments()[j] + shape_anchor);
+ bounding_rect.expand_to(shape->get_segments()[j] + shape_anchor);
+ }
+ if (bounding_rect.has_point(mb->get_position())) {
+ current_shape = polygon;
+ edited_collision_shape = shape;
+ }
+ }
+ }
+ }
+ }
+ }
+ workspace->update();
+ } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ if (edit_mode == EDITMODE_COLLISION) {
+ if (dragging_point >= 0) {
+ dragging_point = -1;
+
+ PoolVector<Vector2> segments;
+ segments.resize(current_shape.size() * 2);
+ PoolVector<Vector2>::Write w = segments.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[(i << 1) + 0] = current_shape[i] - shape_anchor;
+ w[(i << 1) + 1] = current_shape[(i + 1) % current_shape.size()] - shape_anchor;
+ }
+
+ w = PoolVector<Vector2>::Write();
+ edited_collision_shape->set_segments(segments);
+
+ workspace->update();
+ }
+ } else if (edit_mode == EDITMODE_OCCLUSION) {
+ if (dragging_point >= 0) {
+ dragging_point = -1;
+
+ PoolVector<Vector2> polygon;
+ polygon.resize(current_shape.size());
+ PoolVector<Vector2>::Write w = polygon.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[i] = current_shape[i] - shape_anchor;
+ }
+
+ w = PoolVector<Vector2>::Write();
+ edited_occlusion_shape->set_polygon(polygon);
+
+ workspace->update();
+ }
+ } else if (edit_mode == EDITMODE_NAVIGATION) {
+ if (dragging_point >= 0) {
+ dragging_point = -1;
+
+ PoolVector<Vector2> polygon;
+ Vector<int> indices;
+ polygon.resize(current_shape.size());
+ PoolVector<Vector2>::Write w = polygon.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[i] = current_shape[i] - shape_anchor;
+ indices.push_back(i);
+ }
+
+ w = PoolVector<Vector2>::Write();
+ edited_navigation_shape->set_vertices(polygon);
+ edited_navigation_shape->add_polygon(indices);
+
+ workspace->update();
+ }
+ }
+ }
+ } else if (mm.is_valid()) {
+ if (dragging_point >= 0) {
+ current_shape.set(dragging_point, snap_point(mm->get_position()));
+ workspace->update();
+ }
+ }
+ } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) {
+ if (mb.is_valid()) {
+ if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ Vector2 pos = mb->get_position();
+ pos = snap_point(pos);
+ if (creating_shape) {
+ if (current_shape.size() > 0) {
+ if ((pos - current_shape[0]).length_squared() <= MIN_DISTANCE_SQUARED) {
+ if (current_shape.size() > 2) {
+ close_shape(shape_anchor);
+ workspace->update();
+ return;
+ }
+ }
+ }
+ current_shape.push_back(pos);
+ workspace->update();
+ } else {
+ creating_shape = true;
+ current_shape.resize(0);
+ current_shape.push_back(snap_point(pos));
+ }
+ } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
+ if (creating_shape) {
+ close_shape(shape_anchor);
+ }
+ }
+ } else if (mm.is_valid()) {
+ if (creating_shape) {
+ workspace->update();
+ }
+ }
+ }
+ } break;
+ }
+ }
+}
+
+void AutotileEditor::_on_tool_clicked(int p_tool) {
+ if (p_tool == BITMASK_COPY) {
+ bitmask_map_copy = tile_set->autotile_get_bitmask_map(get_current_tile());
+ } else if (p_tool == BITMASK_PASTE) {
+ tile_set->autotile_clear_bitmask_map(get_current_tile());
+ for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) {
+ tile_set->autotile_set_bitmask(get_current_tile(), E->key(), E->value());
+ }
+ workspace->update();
+ } else if (p_tool == BITMASK_CLEAR) {
+ tile_set->autotile_clear_bitmask_map(get_current_tile());
+ workspace->update();
+ } else if (p_tool == SHAPE_DELETE) {
+ if (creating_shape) {
+ creating_shape = false;
+ current_shape.resize(0);
+ workspace->update();
+ } else {
+ switch (edit_mode) {
+ case EDITMODE_COLLISION: {
+ if (!edited_collision_shape.is_null()) {
+ Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(get_current_tile());
+ int index;
+ for (int i = 0; i < sd.size(); i++) {
+ if (sd[i].shape == edited_collision_shape) {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0) {
+ sd.remove(index);
+ tile_set->tile_set_shapes(get_current_tile(), sd);
+ edited_collision_shape = Ref<ConcavePolygonShape2D>();
+ current_shape.resize(0);
+ workspace->update();
+ }
+ }
+ } break;
+ case EDITMODE_NAVIGATION: {
+ if (!edited_navigation_shape.is_null()) {
+ tile_set->autotile_set_navigation_polygon(get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord);
+ edited_navigation_shape = Ref<NavigationPolygon>();
+ current_shape.resize(0);
+ workspace->update();
+ }
+ } break;
+ case EDITMODE_OCCLUSION: {
+ if (!edited_occlusion_shape.is_null()) {
+ tile_set->autotile_set_light_occluder(get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
+ edited_occlusion_shape = Ref<OccluderPolygon2D>();
+ current_shape.resize(0);
+ workspace->update();
+ }
+ } break;
+ }
+ }
+ } else if (p_tool == ZOOM_OUT) {
+ float scale = workspace->get_scale().x;
+ if (scale > 0.1) {
+ scale /= 2;
+ workspace->set_scale(Vector2(scale, scale));
+ workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale);
+ }
+ } else if (p_tool == ZOOM_1) {
+ workspace->set_scale(Vector2(1, 1));
+ workspace_container->set_custom_minimum_size(preview->get_region_rect().size);
+ } else if (p_tool == ZOOM_IN) {
+ float scale = workspace->get_scale().x;
+ scale *= 2;
+ workspace->set_scale(Vector2(scale, scale));
+ workspace_container->set_custom_minimum_size(preview->get_region_rect().size * scale);
+ }
+}
+
+void AutotileEditor::_on_priority_changed(float val) {
+ tile_set->autotile_set_subtile_priority(get_current_tile(), edited_shape_coord, (int)val);
+ workspace->update();
+}
+
+void AutotileEditor::draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted) {
+
+ Vector2 size = tile_set->autotile_get_size(get_current_tile());
+ int spacing = tile_set->autotile_get_spacing(get_current_tile());
+ Rect2 region = tile_set->tile_get_region(get_current_tile());
+ coord.x *= (size.x + spacing);
+ coord.y *= (size.y + spacing);
+ workspace->draw_rect(Rect2(0, 0, region.size.x, coord.y), Color(0.5, 0.5, 0.5, 0.5));
+ workspace->draw_rect(Rect2(0, coord.y, coord.x, size.y), Color(0.5, 0.5, 0.5, 0.5));
+ workspace->draw_rect(Rect2(coord.x + size.x, coord.y, region.size.x - coord.x - size.x, size.y), Color(0.5, 0.5, 0.5, 0.5));
+ workspace->draw_rect(Rect2(0, coord.y + size.y, region.size.x, region.size.y - size.y - coord.y), Color(0.5, 0.5, 0.5, 0.5));
+ coord += Vector2(1, 1);
+ workspace->draw_rect(Rect2(coord, size - Vector2(2, 2)), Color(1, 0, 0), false);
+ for (int i = 0; i < other_highlighted.size(); i++) {
+ coord = other_highlighted[i];
+ coord.x *= (size.x + spacing);
+ coord.y *= (size.y + spacing);
+ coord += Vector2(1, 1);
+ workspace->draw_rect(Rect2(coord, size - Vector2(2, 2)), Color(1, 0, 0), false);
+ }
+}
+
+void AutotileEditor::draw_polygon_shapes() {
+
+ int t_id = get_current_tile();
+ if (t_id < 0)
+ return;
+
+ switch (edit_mode) {
+ case EDITMODE_COLLISION: {
+ Vector<TileSet::ShapeData> sd = tile_set->tile_get_shapes(t_id);
+ for (int i = 0; i < sd.size(); i++) {
+ Vector2 coord = sd[i].autotile_coord;
+ Vector2 anchor = tile_set->autotile_get_size(t_id);
+ anchor.x += tile_set->autotile_get_spacing(t_id);
+ anchor.y += tile_set->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ Ref<ConcavePolygonShape2D> shape = sd[i].shape;
+ if (shape.is_valid()) {
+ Color c_bg;
+ Color c_border;
+ if (coord == edited_shape_coord && sd[i].shape == edited_collision_shape) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
+ }
+ Vector<Vector2> polygon;
+ Vector<Color> colors;
+ if (shape == edited_collision_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ polygon.push_back(current_shape[j]);
+ colors.push_back(c_bg);
+ }
+ } else {
+ for (int j = 0; j < shape->get_segments().size(); j += 2) {
+ polygon.push_back(shape->get_segments()[j] + anchor);
+ colors.push_back(c_bg);
+ }
+ }
+ workspace->draw_polygon(polygon, colors);
+ if (coord == edited_shape_coord) {
+ for (int j = 0; j < shape->get_segments().size(); j += 2) {
+ workspace->draw_line(shape->get_segments()[j] + anchor, shape->get_segments()[j + 1] + anchor, c_border, 1, true);
+ }
+ if (shape == edited_collision_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0));
+ }
+ }
+ }
+ }
+ }
+ } break;
+ case EDITMODE_OCCLUSION: {
+ Map<Vector2, Ref<OccluderPolygon2D> > map = tile_set->autotile_get_light_oclusion_map(t_id);
+ for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
+ Vector2 coord = E->key();
+ Vector2 anchor = tile_set->autotile_get_size(t_id);
+ anchor.x += tile_set->autotile_get_spacing(t_id);
+ anchor.y += tile_set->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ Ref<OccluderPolygon2D> shape = E->value();
+ if (shape.is_valid()) {
+ Color c_bg;
+ Color c_border;
+ if (coord == edited_shape_coord && shape == edited_occlusion_shape) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
+ }
+ Vector<Vector2> polygon;
+ Vector<Color> colors;
+ if (shape == edited_occlusion_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ polygon.push_back(current_shape[j]);
+ colors.push_back(c_bg);
+ }
+ } else {
+ for (int j = 0; j < shape->get_polygon().size(); j++) {
+ polygon.push_back(shape->get_polygon()[j] + anchor);
+ colors.push_back(c_bg);
+ }
+ }
+ workspace->draw_polygon(polygon, colors);
+ if (coord == edited_shape_coord) {
+ for (int j = 0; j < shape->get_polygon().size() - 1; j++) {
+ workspace->draw_line(shape->get_polygon()[j] + anchor, shape->get_polygon()[j + 1] + anchor, c_border, 1, true);
+ }
+ workspace->draw_line(shape->get_polygon()[shape->get_polygon().size() - 1] + anchor, shape->get_polygon()[0] + anchor, c_border, 1, true);
+ if (shape == edited_occlusion_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0));
+ }
+ }
+ }
+ }
+ }
+ } break;
+ case EDITMODE_NAVIGATION: {
+ Map<Vector2, Ref<NavigationPolygon> > map = tile_set->autotile_get_navigation_map(t_id);
+ for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
+ Vector2 coord = E->key();
+ Vector2 anchor = tile_set->autotile_get_size(t_id);
+ anchor.x += tile_set->autotile_get_spacing(t_id);
+ anchor.y += tile_set->autotile_get_spacing(t_id);
+ anchor.x *= coord.x;
+ anchor.y *= coord.y;
+ Ref<NavigationPolygon> shape = E->value();
+ if (shape.is_valid()) {
+ Color c_bg;
+ Color c_border;
+ if (coord == edited_shape_coord && shape == edited_navigation_shape) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
+ }
+ Vector<Vector2> polygon;
+ Vector<Color> colors;
+ if (shape == edited_navigation_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ polygon.push_back(current_shape[j]);
+ colors.push_back(c_bg);
+ }
+ } else if (shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = shape->get_vertices();
+ for (int j = 0; j < shape->get_polygon(0).size(); j++) {
+ polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
+ colors.push_back(c_bg);
+ }
+ }
+ workspace->draw_polygon(polygon, colors);
+ if (coord == edited_shape_coord) {
+ if (shape->get_polygon_count() > 0) {
+ PoolVector<Vector2> vertices = shape->get_vertices();
+ for (int j = 0; j < shape->get_polygon(0).size() - 1; j++) {
+ workspace->draw_line(vertices[shape->get_polygon(0)[j]] + anchor, vertices[shape->get_polygon(0)[j + 1]] + anchor, c_border, 1, true);
+ }
+ if (shape == edited_navigation_shape) {
+ for (int j = 0; j < current_shape.size(); j++) {
+ workspace->draw_circle(current_shape[j], 5, Color(1, 0, 0));
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+ }
+ if (creating_shape) {
+ for (int j = 0; j < current_shape.size() - 1; j++) {
+ workspace->draw_line(current_shape[j], current_shape[j + 1], Color(0, 1, 1), 1, true);
+ }
+ workspace->draw_line(current_shape[current_shape.size() - 1], snap_point(workspace->get_local_mouse_position()), Color(0, 1, 1), 1, true);
+ }
+}
+
+void AutotileEditor::close_shape(const Vector2 &shape_anchor) {
+
+ creating_shape = false;
+
+ if (edit_mode == EDITMODE_COLLISION) {
+ Ref<ConcavePolygonShape2D> shape = memnew(ConcavePolygonShape2D);
+
+ PoolVector<Vector2> segments;
+ segments.resize(current_shape.size() * 2);
+ PoolVector<Vector2>::Write w = segments.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[(i << 1) + 0] = current_shape[i] - shape_anchor;
+ w[(i << 1) + 1] = current_shape[(i + 1) % current_shape.size()] - shape_anchor;
+ }
+
+ w = PoolVector<Vector2>::Write();
+ shape->set_segments(segments);
+
+ tile_set->tile_add_shape(get_current_tile(), shape, Transform2D(), false, edited_shape_coord);
+ edited_collision_shape = shape;
+ tools[TOOL_SELECT]->set_pressed(true);
+ workspace->update();
+ } else if (edit_mode == EDITMODE_OCCLUSION) {
+ Ref<OccluderPolygon2D> shape = memnew(OccluderPolygon2D);
+
+ PoolVector<Vector2> polygon;
+ polygon.resize(current_shape.size());
+ PoolVector<Vector2>::Write w = polygon.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[i] = current_shape[i] - shape_anchor;
+ }
+
+ w = PoolVector<Vector2>::Write();
+ shape->set_polygon(polygon);
+
+ tile_set->autotile_set_light_occluder(get_current_tile(), shape, edited_shape_coord);
+ edited_occlusion_shape = shape;
+ tools[TOOL_SELECT]->set_pressed(true);
+ workspace->update();
+ } else if (edit_mode == EDITMODE_NAVIGATION) {
+ Ref<NavigationPolygon> shape = memnew(NavigationPolygon);
+
+ PoolVector<Vector2> polygon;
+ Vector<int> indices;
+ polygon.resize(current_shape.size());
+ PoolVector<Vector2>::Write w = polygon.write();
+
+ for (int i = 0; i < current_shape.size(); i++) {
+ w[i] = current_shape[i] - shape_anchor;
+ indices.push_back(i);
+ }
+
+ w = PoolVector<Vector2>::Write();
+ shape->set_vertices(polygon);
+ shape->add_polygon(indices);
+ tile_set->autotile_set_navigation_polygon(get_current_tile(), shape, edited_shape_coord);
+ edited_navigation_shape = shape;
+ tools[TOOL_SELECT]->set_pressed(true);
+ workspace->update();
+ }
+}
+
+Vector2 AutotileEditor::snap_point(const Vector2 &point) {
+ Vector2 p = point;
+ Vector2 coord = edited_shape_coord;
+ Vector2 tile_size = tile_set->autotile_get_size(get_current_tile());
+ int spacing = tile_set->autotile_get_spacing(get_current_tile());
+ Vector2 anchor = coord;
+ anchor.x *= (tile_size.x + spacing);
+ anchor.y *= (tile_size.y + spacing);
+ Rect2 region(anchor, tile_size);
+ if (tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) {
+ if (p.x < region.position.x)
+ p.x = region.position.x;
+ if (p.y < region.position.y)
+ p.y = region.position.y;
+ if (p.x > region.position.x + region.size.x)
+ p.x = region.position.x + region.size.x;
+ if (p.y > region.position.y + region.size.y)
+ p.y = region.position.y + region.size.y;
+ }
+ if (tools[SHAPE_SNAP_TO_BITMASK_GRID]->is_pressed()) {
+ Vector2 p2 = p;
+ if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
+ p2.x = Math::stepify(p2.x, tile_size.x / 2);
+ p2.y = Math::stepify(p2.y, tile_size.y / 2);
+ if ((p2 - p).length_squared() <= MAX(tile_size.y / 4, MIN_DISTANCE_SQUARED)) {
+ p = p2;
+ }
+ } else if (tile_set->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_3X3) {
+ p2.x = Math::stepify(p2.x, tile_size.x / 3);
+ p2.y = Math::stepify(p2.y, tile_size.y / 3);
+ if ((p2 - p).length_squared() <= MAX(tile_size.y / 6, MIN_DISTANCE_SQUARED)) {
+ p = p2;
+ }
+ }
+ }
+ p.floor();
+ return p;
+}
+
+void AutotileEditor::edit(Object *p_node) {
+
+ tile_set = Ref<TileSet>(Object::cast_to<TileSet>(p_node));
+ helper->set_tileset(tile_set);
+
+ autotile_list->clear();
+ List<int> ids;
+ tile_set->get_tile_list(&ids);
+ for (List<int>::Element *E = ids.front(); E; E = E->next()) {
+ if (tile_set->tile_get_is_autotile(E->get())) {
+ autotile_list->add_item(tile_set->tile_get_name(E->get()));
+ autotile_list->set_item_metadata(autotile_list->get_item_count() - 1, E->get());
+ autotile_list->set_item_icon(autotile_list->get_item_count() - 1, tile_set->tile_get_texture(E->get()));
+ Rect2 region = tile_set->tile_get_region(E->get());
+ region.size = tile_set->autotile_get_size(E->get());
+ Vector2 pos = tile_set->autotile_get_icon_coordinate(E->get());
+ pos.x *= (tile_set->autotile_get_spacing(E->get()) + region.size.x);
+ pos.y *= (tile_set->autotile_get_spacing(E->get()) + region.size.y);
+ region.position += pos;
+ autotile_list->set_item_icon_region(autotile_list->get_item_count() - 1, region);
+ }
+ }
+ if (autotile_list->get_item_count() > 0) {
+ autotile_list->select(0);
+ _on_autotile_selected(0);
+ }
+ helper->_change_notify("");
+}
+
+int AutotileEditor::get_current_tile() {
+
+ if (autotile_list->get_selected_items().size() == 0)
+ return -1;
+ else
+ return autotile_list->get_item_metadata(autotile_list->get_selected_items()[0]);
+}
+
+void AutotileEditorHelper::set_tileset(const Ref<TileSet> &p_tileset) {
+
+ tile_set = p_tileset;
+}
+
+bool AutotileEditorHelper::_set(const StringName &p_name, const Variant &p_value) {
+
+ if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ return false;
+
+ String name = p_name.operator String();
+ bool v = false;
+ if (name == "bitmask_mode") {
+ tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v);
+ } else if (name.left(7) == "layout/") {
+ tile_set->set(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), p_value, &v);
+ }
+ if (v) {
+ tile_set->_change_notify("");
+ autotile_editor->workspace->update();
+ }
+ return v;
+}
+
+bool AutotileEditorHelper::_get(const StringName &p_name, Variant &r_ret) const {
+
+ if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ return false;
+
+ String name = p_name.operator String();
+ if (name == "bitmask_mode") {
+ r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile/bitmask_mode");
+ } else if (name.left(7) == "layout/") {
+ bool v;
+ r_ret = tile_set->get(String::num(autotile_editor->get_current_tile(), 0) + "/autotile" + name.right(6), &v);
+ return v;
+ }
+}
+
+void AutotileEditorHelper::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ if (autotile_editor->get_current_tile() < 0 || tile_set.is_null())
+ return;
+
+ p_list->push_back(PropertyInfo(Variant::INT, "bitmask_mode", PROPERTY_HINT_ENUM, "2x2,3x3"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, "layout/tile_size"));
+ p_list->push_back(PropertyInfo(Variant::INT, "layout/spacing", PROPERTY_HINT_RANGE, "0,256,1"));
+}
+
+AutotileEditorHelper::AutotileEditorHelper(AutotileEditor *p_autotile_editor) {
+
+ autotile_editor = p_autotile_editor;
}
diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
index 677ee05b55..d60d0d5c3c 100644
--- a/editor/plugins/tile_set_editor_plugin.h
+++ b/editor/plugins/tile_set_editor_plugin.h
@@ -32,10 +32,126 @@
#include "editor/editor_name_dialog.h"
#include "editor/editor_node.h"
+#include "scene/2d/sprite.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/tile_set.h"
+class AutotileEditorHelper;
+class AutotileEditor : public Control {
+
+ friend class TileSetEditorPlugin;
+ friend class AutotileEditorHelper;
+ GDCLASS(AutotileEditor, Control);
+
+ enum EditMode {
+ EDITMODE_ICON,
+ EDITMODE_BITMASK,
+ EDITMODE_COLLISION,
+ EDITMODE_OCCLUSION,
+ EDITMODE_NAVIGATION,
+ EDITMODE_PRIORITY,
+ EDITMODE_MAX
+ };
+
+ enum AutotileToolbars {
+ TOOLBAR_DUMMY,
+ TOOLBAR_BITMASK,
+ TOOLBAR_SHAPE,
+ TOOLBAR_MAX
+ };
+
+ enum AutotileTools {
+ TOOL_SELECT,
+ BITMASK_COPY,
+ BITMASK_PASTE,
+ BITMASK_CLEAR,
+ SHAPE_NEW_POLYGON,
+ SHAPE_DELETE,
+ SHAPE_CREATE_FROM_BITMASK,
+ SHAPE_CREATE_FROM_NOT_BITMASK,
+ SHAPE_KEEP_INSIDE_TILE,
+ SHAPE_SNAP_TO_BITMASK_GRID,
+ ZOOM_OUT,
+ ZOOM_1,
+ ZOOM_IN,
+ TOOL_MAX
+ };
+
+ Ref<TileSet> tile_set;
+ Ref<ConcavePolygonShape2D> edited_collision_shape;
+ Ref<OccluderPolygon2D> edited_occlusion_shape;
+ Ref<NavigationPolygon> edited_navigation_shape;
+
+ EditorNode *editor;
+
+ int current_item_index;
+ Sprite *preview;
+ Control *workspace_container;
+ Control *workspace;
+ Button *tool_editmode[EDITMODE_MAX];
+ HBoxContainer *tool_containers[TOOLBAR_MAX];
+ HBoxContainer *toolbar;
+ ToolButton *tools[TOOL_MAX];
+ SpinBox *spin_priority;
+ EditMode edit_mode;
+
+ bool creating_shape;
+ int dragging_point;
+ Vector2 edited_shape_coord;
+ PoolVector2Array current_shape;
+ Map<Vector2, uint16_t> bitmask_map_copy;
+
+ Control *side_panel;
+ ItemList *autotile_list;
+ PropertyEditor *property_editor;
+ AutotileEditorHelper *helper;
+
+ AutotileEditor(EditorNode *p_editor);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+private:
+ void _on_autotile_selected(int p_index);
+ void _on_edit_mode_changed(int p_edit_mode);
+ void _on_workspace_draw();
+ void _on_workspace_input(const Ref<InputEvent> &p_ie);
+ void _on_tool_clicked(int p_tool);
+ void _on_priority_changed(float val);
+
+ void draw_highlight_tile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>());
+ void draw_grid(const Vector2 &size, int spacing);
+ void draw_polygon_shapes();
+ void close_shape(const Vector2 &shape_anchor);
+ Vector2 snap_point(const Vector2 &point);
+
+ void edit(Object *p_node);
+ int get_current_tile();
+};
+
+class AutotileEditorHelper : public Object {
+
+ friend class AutotileEditor;
+ GDCLASS(AutotileEditorHelper, Object);
+
+ Ref<TileSet> tile_set;
+ AutotileEditor *autotile_editor;
+
+public:
+ void set_tileset(const Ref<TileSet> &p_tileset);
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ AutotileEditorHelper(AutotileEditor *p_autotile_editor);
+};
+
class TileSetEditor : public Control {
+ friend class TileSetEditorPlugin;
GDCLASS(TileSetEditor, Control);
Ref<TileSet> tileset;
@@ -77,8 +193,11 @@ class TileSetEditorPlugin : public EditorPlugin {
GDCLASS(TileSetEditorPlugin, EditorPlugin);
TileSetEditor *tileset_editor;
+ AutotileEditor *autotile_editor;
EditorNode *editor;
+ ToolButton *autotile_button;
+
public:
virtual String get_name() const { return "TileSet"; }
bool has_main_screen() const { return false; }