summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_bezier_editor.cpp10
-rw-r--r--editor/animation_track_editor.cpp28
-rw-r--r--editor/animation_track_editor_plugins.cpp2
-rw-r--r--editor/editor_about.cpp29
-rw-r--r--editor/editor_about.h3
-rw-r--r--editor/editor_data.cpp12
-rw-r--r--editor/editor_data.h4
-rw-r--r--editor/editor_inspector.cpp16
-rw-r--r--editor/editor_log.cpp258
-rw-r--r--editor/editor_log.h108
-rw-r--r--editor/editor_node.cpp44
-rw-r--r--editor/editor_node.h4
-rw-r--r--editor/editor_plugin.cpp10
-rw-r--r--editor/editor_plugin.h3
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/editor_spin_slider.cpp3
-rw-r--r--editor/icons/CombineLines.svg1
-rw-r--r--editor/import/resource_importer_scene.cpp149
-rw-r--r--editor/import/resource_importer_scene.h2
-rw-r--r--editor/import/scene_importer_mesh.cpp130
-rw-r--r--editor/import/scene_importer_mesh.h2
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp17
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp12
-rw-r--r--editor/project_manager.cpp32
-rw-r--r--editor/project_manager.h2
-rw-r--r--editor/settings_config_dialog.cpp21
27 files changed, 618 insertions, 288 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index ab8ae71904..67b52bf0ca 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -762,7 +762,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
undo_redo->commit_action();
//then attempt to move
@@ -857,7 +857,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, newtime);
AnimMoveRestore amr;
amr.key = animation->track_get_key_value(track, idx);
@@ -888,7 +888,7 @@ void AnimationBezierTrackEdit::_gui_input(const Ref<InputEvent> &p_event) {
if (newpos<0)
continue; //no remove what no inserted
*/
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, newpos);
}
// 5-(undo) reinsert keys
@@ -1038,7 +1038,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
undo_redo->create_action(TTR("Add Bezier Point"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
undo_redo->commit_action();
} break;
@@ -1074,7 +1074,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
int existing_idx = animation->track_find_key(track, dst_time, true);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get()));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, dst_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, dst_time);
Pair<int, float> p;
p.first = track;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 9db2f0a287..89f77f3c77 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -134,7 +134,7 @@ public:
undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time);
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
@@ -758,7 +758,7 @@ public:
undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, key);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, new_time, val, trans);
undo_redo->add_do_method(this, "_key_ofs_changed", animation, key_ofs, new_time);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", track, new_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, new_time);
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, key_ofs, val, trans);
undo_redo->add_undo_method(this, "_key_ofs_changed", animation, new_time, key_ofs);
@@ -3979,7 +3979,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
undo_redo->add_undo_method(animation.ptr(), "remove_track", animation->get_track_count());
p_next_tracks.normal++;
} else {
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_id.track_idx, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_id.track_idx, time);
int existing = animation->track_find_key(p_id.track_idx, time, true);
if (existing != -1) {
Variant v = animation->track_get_key_value(p_id.track_idx, existing);
@@ -4568,7 +4568,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Transform Track Key"));
undo_redo->add_do_method(animation.ptr(), "transform_track_insert_key", p_track, p_ofs, loc, rot, scale);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4580,7 +4580,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, value);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4611,7 +4611,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
@@ -4623,7 +4623,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, ak);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
case Animation::TYPE_ANIMATION: {
@@ -4631,7 +4631,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
undo_redo->create_action(TTR("Add Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, anim);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", p_track, p_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs);
undo_redo->commit_action();
} break;
}
@@ -4669,7 +4669,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
undo_redo->create_action(TTR("Add Method Track Key"));
undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", insert_key_from_track_call_track, insert_key_from_track_call_ofs);
undo_redo->commit_action();
return;
@@ -4879,7 +4879,7 @@ void AnimationTrackEditor::_move_selection_commit() {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
_AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->key().track, idx);
@@ -4899,7 +4899,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 4 - (undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = snap_time(E->get().pos + motion);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
// 5 - (undo) reinsert keys
@@ -5096,7 +5096,7 @@ void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) {
int existing_idx = animation->track_find_key(dst_track, dst_time, true);
undo_redo->add_do_method(animation.ptr(), "track_insert_key", dst_track, dst_time, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", dst_track, dst_time);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", dst_track, dst_time);
Pair<int, float> p;
p.first = dst_track;
@@ -5344,7 +5344,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
continue; //already in selection, don't save
}
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newtime);
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newtime);
_AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->key().track, idx);
@@ -5365,7 +5365,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
// 4-(undo) remove inserted keys
for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = _NEW_POS(E->get().pos);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_position", E->key().track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
// 5-(undo) reinsert keys
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 506a327ffc..ae79299331 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -1024,7 +1024,7 @@ void AnimationTrackEditTypeAudio::drop_data(const Point2 &p_point, const Variant
get_undo_redo()->create_action(TTR("Add Audio Track Clip"));
get_undo_redo()->add_do_method(get_animation().ptr(), "audio_track_insert_key", get_track(), ofs, stream);
- get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_position", get_track(), ofs);
+ get_undo_redo()->add_undo_method(get_animation().ptr(), "track_remove_key_at_time", get_track(), ofs);
get_undo_redo()->commit_action();
update();
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index d962658484..7527514dca 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -37,6 +37,9 @@
#include "core/version.h"
#include "core/version_hash.gen.h"
+// The metadata key used to store and retrieve the version text to copy to the clipboard.
+static const String META_TEXT_TO_COPY = "text_to_copy";
+
void EditorAbout::_theme_changed() {
const Ref<Font> font = get_theme_font("source", "EditorFonts");
const int font_size = get_theme_font_size("source_size", "EditorFonts");
@@ -63,7 +66,12 @@ void EditorAbout::_license_tree_selected() {
_tpl_text->set_text(selected->get_metadata(0));
}
+void EditorAbout::_version_button_pressed() {
+ DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
+}
+
void EditorAbout::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_version_button_pressed"), &EditorAbout::_version_button_pressed);
}
TextureRect *EditorAbout::get_logo() const {
@@ -124,17 +132,32 @@ EditorAbout::EditorAbout() {
_logo = memnew(TextureRect);
hbc->add_child(_logo);
+ VBoxContainer *version_info_vbc = memnew(VBoxContainer);
+
+ // Add a dummy control node for spacing.
+ Control *v_spacer = memnew(Control);
+ version_info_vbc->add_child(v_spacer);
+
+ version_btn = memnew(LinkButton);
String hash = String(VERSION_HASH);
if (hash.length() != 0) {
hash = "." + hash.left(9);
}
+ version_btn->set_text(VERSION_FULL_NAME + hash);
+ // Set the text to copy in metadata as it slightly differs from the button's text.
+ version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
+ version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+ version_btn->set_tooltip(TTR("Click to copy."));
+ version_btn->connect("pressed", callable_mp(this, &EditorAbout::_version_button_pressed));
+ version_info_vbc->add_child(version_btn);
Label *about_text = memnew(Label);
about_text->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- about_text->set_text(VERSION_FULL_NAME + hash +
- String::utf8("\n\xc2\xa9 2007-2021 Juan Linietsky, Ariel Manzur.\n\xc2\xa9 2014-2021 ") +
+ about_text->set_text(String::utf8("\xc2\xa9 2007-2021 Juan Linietsky, Ariel Manzur.\n\xc2\xa9 2014-2021 ") +
TTR("Godot Engine contributors") + "\n");
- hbc->add_child(about_text);
+ version_info_vbc->add_child(about_text);
+
+ hbc->add_child(version_info_vbc);
TabContainer *tc = memnew(TabContainer);
tc->set_custom_minimum_size(Size2(950, 400) * EDSCALE);
diff --git a/editor/editor_about.h b/editor/editor_about.h
index 2823220a8a..b76a2ada34 100644
--- a/editor/editor_about.h
+++ b/editor/editor_about.h
@@ -34,6 +34,7 @@
#include "scene/gui/control.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
+#include "scene/gui/link_button.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/scroll_container.h"
#include "scene/gui/separator.h"
@@ -53,8 +54,10 @@ class EditorAbout : public AcceptDialog {
private:
void _license_tree_selected();
+ void _version_button_pressed();
ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0);
+ LinkButton *version_btn;
Tree *_tpl_tree;
RichTextLabel *_license_text;
RichTextLabel *_tpl_text;
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index fa4703d425..6405af3876 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -426,6 +426,18 @@ UndoRedo &EditorData::get_undo_redo() {
return undo_redo;
}
+void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) {
+ undo_redo_callbacks.push_back(p_callable);
+}
+
+void EditorData::remove_undo_redo_inspector_hook_callback(Callable p_callable) {
+ undo_redo_callbacks.erase(p_callable);
+}
+
+const Vector<Callable> EditorData::get_undo_redo_inspector_hook_callback() {
+ return undo_redo_callbacks;
+}
+
void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) {
p_plugin->undo_redo = nullptr;
editor_plugins.erase(p_plugin);
diff --git a/editor/editor_data.h b/editor/editor_data.h
index dbe729d9d9..2ece94d6a3 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -132,6 +132,7 @@ private:
List<PropertyData> clipboard;
UndoRedo undo_redo;
+ Vector<Callable> undo_redo_callbacks;
void _cleanup_history();
@@ -166,6 +167,9 @@ public:
EditorPlugin *get_editor_plugin(int p_idx);
UndoRedo &get_undo_redo();
+ void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks shoud have 4 args: (Object* undo_redo, Object *modified_object, String property, Varian new_value)
+ void remove_undo_redo_inspector_hook_callback(Callable p_callable);
+ const Vector<Callable> get_undo_redo_inspector_hook_callback();
void save_editor_global_states();
void restore_editor_global_states();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 738b2f9f82..5bb3c8b4d0 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -2266,6 +2266,22 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
undo_redo->add_do_property(object, p_name, p_value);
undo_redo->add_undo_property(object, p_name, object->get(p_name));
+ Variant v_undo_redo = (Object *)undo_redo;
+ Variant v_object = object;
+ Variant v_name = p_name;
+ for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback().size(); i++) {
+ const Callable &callback = EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback()[i];
+
+ const Variant *p_arguments[] = { &v_undo_redo, &v_object, &v_name, &p_value };
+ Variant return_value;
+ Callable::CallError call_error;
+
+ callback.call(p_arguments, 4, return_value, call_error);
+ if (call_error.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Invalid UndoRedo callback.");
+ }
+ }
+
if (p_refresh_all) {
undo_redo->add_do_method(this, "_edit_request_change", object, "");
undo_redo->add_undo_method(this, "_edit_request_change", object, "");
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 7b94016fb6..9f188b53c4 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -63,6 +63,18 @@ void EditorLog::_notification(int p_what) {
log->add_theme_font_override("normal_font", get_theme_font("output_source", "EditorFonts"));
log->add_theme_font_size_override("normal_font_size", get_theme_font_size("output_source_size", "EditorFonts"));
log->add_theme_color_override("selection_color", get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4));
+ log->add_theme_font_override("bold_font", get_theme_font("bold", "EditorFonts"));
+
+ type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon("Popup", "EditorIcons"));
+ type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon("StatusError", "EditorIcons"));
+ type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon("StatusWarning", "EditorIcons"));
+ type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon("Edit", "EditorIcons"));
+
+ clear_button->set_icon(get_theme_icon("Clear", "EditorIcons"));
+ copy_button->set_icon(get_theme_icon("ActionCopy", "EditorIcons"));
+ collapse_button->set_icon(get_theme_icon("CombineLines", "EditorIcons"));
+ show_search_button->set_icon(get_theme_icon("Search", "EditorIcons"));
+
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
Ref<Font> df_output_code = get_theme_font("output_source", "EditorFonts");
if (df_output_code.is_valid()) {
@@ -75,8 +87,15 @@ void EditorLog::_notification(int p_what) {
}
}
+void EditorLog::_set_collapse(bool p_collapse) {
+ collapse = p_collapse;
+ _rebuild_log();
+}
+
void EditorLog::_clear_request() {
log->clear();
+ messages.clear();
+ _reset_message_counts();
tool_button->set_icon(Ref<Texture2D>());
}
@@ -96,13 +115,83 @@ void EditorLog::clear() {
_clear_request();
}
-void EditorLog::copy() {
- _copy_request();
+void EditorLog::_process_message(const String &p_msg, MessageType p_type) {
+ if (messages.size() > 0 && messages[messages.size() - 1].text == p_msg) {
+ // If previous message is the same as the new one, increase previous count rather than adding another
+ // instance to the messages list.
+ LogMessage &previous = messages.write[messages.size() - 1];
+ previous.count++;
+
+ _add_log_line(previous, collapse);
+ } else {
+ // Different message to the previous one received.
+ LogMessage message(p_msg, p_type);
+ _add_log_line(message);
+ messages.push_back(message);
+ }
+
+ type_filter_map[p_type]->set_message_count(type_filter_map[p_type]->get_message_count() + 1);
}
void EditorLog::add_message(const String &p_msg, MessageType p_type) {
- bool restore = p_type != MSG_TYPE_STD;
- switch (p_type) {
+ // Make text split by new lines their own message.
+ // See #41321 for reasoning. At time of writing, multiple print()'s in running projects
+ // get grouped together and sent to the editor log as one message. This can mess with the
+ // search functionality (see the comments on the PR above for more details). This behaviour
+ // also matches that of other IDE's.
+ Vector<String> lines = p_msg.split("\n", false);
+
+ for (int i = 0; i < lines.size(); i++) {
+ _process_message(lines[i], p_type);
+ }
+}
+
+void EditorLog::set_tool_button(Button *p_tool_button) {
+ tool_button = p_tool_button;
+}
+
+void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
+ EditorLog *self = (EditorLog *)p_self;
+ self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
+}
+
+void EditorLog::_rebuild_log() {
+ log->clear();
+
+ for (int msg_idx = 0; msg_idx < messages.size(); msg_idx++) {
+ LogMessage msg = messages[msg_idx];
+
+ if (collapse) {
+ // If collapsing, only log one instance of the message.
+ _add_log_line(msg);
+ } else {
+ // If not collapsing, log each instance on a line.
+ for (int i = 0; i < msg.count; i++) {
+ _add_log_line(msg);
+ }
+ }
+ }
+}
+
+void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
+ // Only add the message to the log if it passes the filters.
+ bool filter_active = type_filter_map[p_message.type]->active;
+ String search_text = search_box->get_text();
+ bool search_match = search_text == String() || p_message.text.findn(search_text) > -1;
+
+ if (!filter_active || !search_match) {
+ return;
+ }
+
+ if (p_replace_previous) {
+ // Remove last line if replacing, as it will be replace by the next added line.
+ log->remove_line(log->get_line_count() - 1);
+ log->increment_line_count();
+ } else {
+ log->add_newline();
+ }
+
+ switch (p_message.type) {
case MSG_TYPE_STD: {
} break;
case MSG_TYPE_ERROR: {
@@ -125,21 +214,42 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) {
} break;
}
- log->add_text(p_msg);
- log->add_newline();
+ // If collapsing, add the count of this message in bold at the start of the line.
+ if (collapse && p_message.count > 1) {
+ log->push_bold();
+ log->add_text(vformat("(%s) ", itos(p_message.count)));
+ log->pop();
+ }
- if (restore) {
+ log->add_text(p_message.text);
+
+ // Need to use pop() to exit out of the RichTextLabels current "push" stack.
+ // We only "push" in the above switch when message type != STD, so only pop when that is the case.
+ if (p_message.type != MSG_TYPE_STD) {
log->pop();
}
}
-void EditorLog::set_tool_button(Button *p_tool_button) {
- tool_button = p_tool_button;
+void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) {
+ type_filter_map[p_message_type]->active = p_active;
+ _rebuild_log();
}
-void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) {
- EditorLog *self = (EditorLog *)p_self;
- self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR);
+void EditorLog::_set_search_visible(bool p_visible) {
+ search_box->set_visible(p_visible);
+ if (p_visible) {
+ search_box->grab_focus();
+ }
+}
+
+void EditorLog::_search_changed(const String &p_text) {
+ _rebuild_log();
+}
+
+void EditorLog::_reset_message_counts() {
+ for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) {
+ E->value()->set_message_count(0);
+ }
}
void EditorLog::_bind_methods() {
@@ -148,37 +258,108 @@ void EditorLog::_bind_methods() {
}
EditorLog::EditorLog() {
- VBoxContainer *vb = this;
-
- HBoxContainer *hb = memnew(HBoxContainer);
- vb->add_child(hb);
- title = memnew(Label);
- title->set_text(TTR("Output:"));
- title->set_h_size_flags(SIZE_EXPAND_FILL);
- hb->add_child(title);
-
- copybutton = memnew(Button);
- hb->add_child(copybutton);
- copybutton->set_text(TTR("Copy"));
- copybutton->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C));
- copybutton->set_shortcut_context(this);
- copybutton->connect("pressed", callable_mp(this, &EditorLog::_copy_request));
-
- clearbutton = memnew(Button);
- hb->add_child(clearbutton);
- clearbutton->set_text(TTR("Clear"));
- clearbutton->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K));
- clearbutton->set_shortcut_context(this);
- clearbutton->connect("pressed", callable_mp(this, &EditorLog::_clear_request));
+ HBoxContainer *hb = this;
+ VBoxContainer *vb_left = memnew(VBoxContainer);
+ vb_left->set_custom_minimum_size(Size2(0, 180) * EDSCALE);
+ vb_left->set_v_size_flags(SIZE_EXPAND_FILL);
+ vb_left->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(vb_left);
+
+ // Log - Rich Text Label.
log = memnew(RichTextLabel);
log->set_scroll_follow(true);
log->set_selection_enabled(true);
log->set_focus_mode(FOCUS_CLICK);
- log->set_custom_minimum_size(Size2(0, 180) * EDSCALE);
log->set_v_size_flags(SIZE_EXPAND_FILL);
log->set_h_size_flags(SIZE_EXPAND_FILL);
- vb->add_child(log);
+ vb_left->add_child(log);
+
+ // Search box
+ search_box = memnew(LineEdit);
+ search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ search_box->set_placeholder(TTR("Filter messages"));
+ search_box->set_right_icon(get_theme_icon("Search", "EditorIcons"));
+ search_box->set_clear_button_enabled(true);
+ search_box->set_visible(true);
+ search_box->connect("text_changed", callable_mp(this, &EditorLog::_search_changed));
+ vb_left->add_child(search_box);
+
+ VBoxContainer *vb_right = memnew(VBoxContainer);
+ hb->add_child(vb_right);
+
+ // Tools grid
+ HBoxContainer *hb_tools = memnew(HBoxContainer);
+ hb_tools->set_h_size_flags(SIZE_SHRINK_CENTER);
+ vb_right->add_child(hb_tools);
+
+ // Clear.
+ clear_button = memnew(Button);
+ clear_button->set_flat(true);
+ clear_button->set_focus_mode(FOCUS_NONE);
+ clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K));
+ clear_button->set_shortcut_context(this);
+ clear_button->connect("pressed", callable_mp(this, &EditorLog::_clear_request));
+ hb_tools->add_child(clear_button);
+
+ // Copy.
+ copy_button = memnew(Button);
+ copy_button->set_flat(true);
+ copy_button->set_focus_mode(FOCUS_NONE);
+ copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KEY_MASK_CMD | KEY_C));
+ copy_button->set_shortcut_context(this);
+ copy_button->connect("pressed", callable_mp(this, &EditorLog::_copy_request));
+ hb_tools->add_child(copy_button);
+
+ // A second hbox to make a 2x2 grid of buttons.
+ HBoxContainer *hb_tools2 = memnew(HBoxContainer);
+ hb_tools2->set_h_size_flags(SIZE_SHRINK_CENTER);
+ vb_right->add_child(hb_tools2);
+
+ // Collapse.
+ collapse_button = memnew(Button);
+ collapse_button->set_flat(true);
+ collapse_button->set_focus_mode(FOCUS_NONE);
+ collapse_button->set_tooltip(TTR("Collapse duplicate messages into one log entry. Shows number of occurences."));
+ collapse_button->set_toggle_mode(true);
+ collapse_button->set_pressed(false);
+ collapse_button->connect("toggled", callable_mp(this, &EditorLog::_set_collapse));
+ hb_tools2->add_child(collapse_button);
+
+ // Show Search.
+ show_search_button = memnew(Button);
+ show_search_button->set_flat(true);
+ show_search_button->set_focus_mode(FOCUS_NONE);
+ show_search_button->set_toggle_mode(true);
+ show_search_button->set_pressed(true);
+ show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTR("Open the search box."), KEY_MASK_CMD | KEY_F));
+ show_search_button->set_shortcut_context(this);
+ show_search_button->connect("toggled", callable_mp(this, &EditorLog::_set_search_visible));
+ hb_tools2->add_child(show_search_button);
+
+ // Message Type Filters.
+ vb_right->add_child(memnew(HSeparator));
+
+ LogFilter *std_filter = memnew(LogFilter(MSG_TYPE_STD));
+ std_filter->initialize_button("Toggle visibility of standard output messages.", callable_mp(this, &EditorLog::_set_filter_active));
+ vb_right->add_child(std_filter->toggle_button);
+ type_filter_map.insert(MSG_TYPE_STD, std_filter);
+
+ LogFilter *error_filter = memnew(LogFilter(MSG_TYPE_ERROR));
+ error_filter->initialize_button("Toggle visibility of errors.", callable_mp(this, &EditorLog::_set_filter_active));
+ vb_right->add_child(error_filter->toggle_button);
+ type_filter_map.insert(MSG_TYPE_ERROR, error_filter);
+
+ LogFilter *warning_filter = memnew(LogFilter(MSG_TYPE_WARNING));
+ warning_filter->initialize_button("Toggle visibility of warnings.", callable_mp(this, &EditorLog::_set_filter_active));
+ vb_right->add_child(warning_filter->toggle_button);
+ type_filter_map.insert(MSG_TYPE_WARNING, warning_filter);
+
+ LogFilter *editor_filter = memnew(LogFilter(MSG_TYPE_EDITOR));
+ editor_filter->initialize_button("Toggle visibility of editor messages.", callable_mp(this, &EditorLog::_set_filter_active));
+ vb_right->add_child(editor_filter->toggle_button);
+ type_filter_map.insert(MSG_TYPE_EDITOR, editor_filter);
+
add_message(VERSION_FULL_NAME " (c) 2007-2021 Juan Linietsky, Ariel Manzur & Godot Contributors.");
eh.errfunc = _error_handler;
@@ -187,8 +368,6 @@ EditorLog::EditorLog() {
current = Thread::get_caller_id();
- add_theme_constant_override("separation", get_theme_constant("separation", "VBoxContainer"));
-
EditorNode::get_undo_redo()->set_commit_notify_callback(_undo_redo_cbk, this);
}
@@ -197,4 +376,7 @@ void EditorLog::deinit() {
}
EditorLog::~EditorLog() {
+ for (Map<MessageType, LogFilter *>::Element *E = type_filter_map.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
}
diff --git a/editor/editor_log.h b/editor/editor_log.h
index 79dfb3ffaa..89d00d0fa0 100644
--- a/editor/editor_log.h
+++ b/editor/editor_log.h
@@ -36,19 +36,92 @@
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/texture_button.h"
#include "scene/gui/texture_rect.h"
-class EditorLog : public VBoxContainer {
- GDCLASS(EditorLog, VBoxContainer);
+class EditorLog : public HBoxContainer {
+ GDCLASS(EditorLog, HBoxContainer);
+
+public:
+ enum MessageType {
+ MSG_TYPE_STD,
+ MSG_TYPE_ERROR,
+ MSG_TYPE_WARNING,
+ MSG_TYPE_EDITOR,
+ };
+
+private:
+ struct LogMessage {
+ String text;
+ MessageType type;
+ int count = 1;
+
+ LogMessage() {}
+
+ LogMessage(const String p_text, MessageType p_type) :
+ text(p_text),
+ type(p_type) {
+ }
+ };
+
+ // Encapsulates all data and functionality regarding filters.
+ struct LogFilter {
+ private:
+ // Force usage of set method since it has functionality built-in.
+ int message_count = 0;
+
+ public:
+ MessageType type;
+ Button *toggle_button = nullptr;
+ bool active = true;
+
+ void initialize_button(const String &p_tooltip, Callable p_toggled_callback) {
+ toggle_button = memnew(Button);
+ toggle_button->set_toggle_mode(true);
+ toggle_button->set_pressed(true);
+ toggle_button->set_text(itos(message_count));
+ toggle_button->set_tooltip(TTR(p_tooltip));
+ // Don't tint the icon even when in "pressed" state.
+ toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1));
+ toggle_button->set_focus_mode(FOCUS_NONE);
+ // When toggled call the callback and pass the MessageType this button is for.
+ toggle_button->connect("toggled", p_toggled_callback, varray(type));
+ }
+
+ int get_message_count() {
+ return message_count;
+ }
+
+ void set_message_count(int p_count) {
+ message_count = p_count;
+ toggle_button->set_text(itos(message_count));
+ }
+
+ LogFilter(MessageType p_type) :
+ type(p_type) {
+ }
+ };
+
+ Vector<LogMessage> messages;
+ // Maps MessageTypes to LogFilters for convenient access and storage (don't need 1 member per filter).
+ Map<MessageType, LogFilter *> type_filter_map;
- Button *clearbutton;
- Button *copybutton;
- Label *title;
RichTextLabel *log;
- HBoxContainer *title_hb;
+
+ Button *clear_button;
+ Button *copy_button;
+
+ Button *collapse_button;
+ bool collapse = false;
+
+ Button *show_search_button;
+ LineEdit *search_box;
+
+ // Reference to the "Output" button on the toolbar so we can update it's icon when
+ // Warnings or Errors are encounetered.
Button *tool_button;
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, ErrorHandlerType p_type);
@@ -62,26 +135,33 @@ class EditorLog : public VBoxContainer {
void _copy_request();
static void _undo_redo_cbk(void *p_self, const String &p_name);
+ void _rebuild_log();
+ void _add_log_line(LogMessage &p_message, bool p_replace_previous = false);
+
+ void _set_filter_active(bool p_active, MessageType p_message_type);
+ void _set_search_visible(bool p_visible);
+ void _search_changed(const String &p_text);
+
+ void _process_message(const String &p_msg, MessageType p_type);
+ void _reset_message_counts();
+
+ void _set_collapse(bool p_collapse);
+
protected:
static void _bind_methods();
void _notification(int p_what);
public:
- enum MessageType {
- MSG_TYPE_STD,
- MSG_TYPE_ERROR,
- MSG_TYPE_WARNING,
- MSG_TYPE_EDITOR
- };
-
void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD);
void set_tool_button(Button *p_tool_button);
void deinit();
void clear();
- void copy();
+
EditorLog();
~EditorLog();
};
+VARIANT_ENUM_CAST(EditorLog::MessageType);
+
#endif // EDITOR_LOG_H
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6137617564..8eeabf9cfd 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -46,11 +46,13 @@
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "core/version.h"
+#include "core/version_hash.gen.h"
#include "main/main.h"
#include "scene/gui/center_container.h"
#include "scene/gui/control.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
+#include "scene/gui/link_button.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/panel_container.h"
@@ -184,6 +186,9 @@
EditorNode *EditorNode::singleton = nullptr;
+// The metadata key used to store and retrieve the version text to copy to the clipboard.
+static const String META_TEXT_TO_COPY = "text_to_copy";
+
void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) {
// Keep track of a list of "index sets," i.e. sets of indices
// within disambiguated_scene_names which contain the same name.
@@ -989,6 +994,10 @@ void EditorNode::_reload_project_settings() {
void EditorNode::_vp_resized() {
}
+void EditorNode::_version_button_pressed() {
+ DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
+}
+
void EditorNode::_node_renamed() {
if (get_inspector()) {
get_inspector()->update_tree();
@@ -1399,8 +1408,9 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
} else {
// The 3D editor may be disabled as a feature, but scenes can still be opened.
// This check prevents the preview from regenerating in case those scenes are then saved.
+ // The preview will be generated if no feature profile is set (as the 3D editor is enabled by default).
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
- if (profile.is_valid() && !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
+ if (!profile.is_valid() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)) {
img = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node()->get_texture()->get_image();
}
}
@@ -4855,7 +4865,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
_scene_tab_closed(scene_tabs->get_hovered_tab());
}
} else {
- if ((mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_doubleclick()) || (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed())) {
+ if ((mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_double_click()) || (mb->get_button_index() == MOUSE_BUTTON_MIDDLE && mb->is_pressed())) {
_menu_option_confirm(FILE_NEW_SCENE, true);
}
}
@@ -5559,6 +5569,8 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_screenshot", &EditorNode::_screenshot);
ClassDB::bind_method("_save_screenshot", &EditorNode::_save_screenshot);
+ ClassDB::bind_method("_version_button_pressed", &EditorNode::_version_button_pressed);
+
ADD_SIGNAL(MethodInfo("play_pressed"));
ADD_SIGNAL(MethodInfo("pause_pressed"));
ADD_SIGNAL(MethodInfo("stop_pressed"));
@@ -6617,11 +6629,31 @@ EditorNode::EditorNode() {
bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
bottom_panel_hb->add_child(bottom_panel_hb_editors);
- version_label = memnew(Label);
- version_label->set_text(VERSION_FULL_CONFIG);
+ VBoxContainer *version_info_vbc = memnew(VBoxContainer);
+ bottom_panel_hb->add_child(version_info_vbc);
+
+ // Add a dummy control node for vertical spacing.
+ Control *v_spacer = memnew(Control);
+ version_info_vbc->add_child(v_spacer);
+
+ version_btn = memnew(LinkButton);
+ version_btn->set_text(VERSION_FULL_CONFIG);
+ String hash = String(VERSION_HASH);
+ if (hash.length() != 0) {
+ hash = "." + hash.left(9);
+ }
+ // Set the text to copy in metadata as it slightly differs from the button's text.
+ version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
// Fade out the version label to be less prominent, but still readable
- version_label->set_self_modulate(Color(1, 1, 1, 0.6));
- bottom_panel_hb->add_child(version_label);
+ version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
+ version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+ version_btn->set_tooltip(TTR("Click to copy."));
+ version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed));
+ version_info_vbc->add_child(version_btn);
+
+ // Add a dummy control node for horizontal spacing.
+ Control *h_spacer = memnew(Control);
+ bottom_panel_hb->add_child(h_spacer);
bottom_panel_raise = memnew(Button);
bottom_panel_raise->set_flat(true);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 7e16936f5d..d06851cb4f 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -40,6 +40,7 @@
#include "editor/inspector_dock.h"
#include "editor/property_editor.h"
#include "editor/scene_tree_dock.h"
+#include "scene/gui/link_button.h"
typedef void (*EditorNodeInitCallback)();
typedef void (*EditorPluginInitializeCallback)();
@@ -424,7 +425,7 @@ private:
HBoxContainer *bottom_panel_hb;
HBoxContainer *bottom_panel_hb_editors;
VBoxContainer *bottom_panel_vb;
- Label *version_label;
+ LinkButton *version_btn;
Button *bottom_panel_raise;
Tree *disk_changed_list;
@@ -477,6 +478,7 @@ private:
void _close_messages();
void _show_messages();
void _vp_resized();
+ void _version_button_pressed();
int _save_external_resources();
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index eabcbacd9a..6b96cb0f5c 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -703,6 +703,14 @@ bool EditorPlugin::get_remove_list(List<Node *> *p_list) {
void EditorPlugin::restore_global_state() {}
void EditorPlugin::save_global_state() {}
+void EditorPlugin::add_undo_redo_inspector_hook_callback(Callable p_callable) {
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(p_callable);
+}
+
+void EditorPlugin::remove_undo_redo_inspector_hook_callback(Callable p_callable) {
+ EditorNode::get_singleton()->get_editor_data().remove_undo_redo_inspector_hook_callback(p_callable);
+}
+
void EditorPlugin::add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser) {
EditorTranslationParser::get_singleton()->add_parser(p_parser, EditorTranslationParser::CUSTOM);
}
@@ -862,6 +870,8 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel);
ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::_get_undo_redo);
+ ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback);
+ ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback);
ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
ClassDB::bind_method(D_METHOD("add_translation_parser_plugin", "parser"), &EditorPlugin::add_translation_parser_plugin);
ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 67b163eabf..37412e5ebe 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -225,6 +225,9 @@ public:
EditorInterface *get_editor_interface();
ScriptCreateDialog *get_script_create_dialog();
+ void add_undo_redo_inspector_hook_callback(Callable p_callable);
+ void remove_undo_redo_inspector_hook_callback(Callable p_callable);
+
int update_overlays() const;
void queue_save_layout();
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 652deb1804..47c0e31da6 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -929,7 +929,7 @@ EditorPropertyFloat::EditorPropertyFloat() {
void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
const Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid()) {
- if (mb->is_doubleclick() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_double_click() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
_setup_spin();
}
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index 8577ccb9db..dba53a9708 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -70,7 +70,6 @@ void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
grabbing_spinner_dist_cache = 0;
pre_grab_value = get_value();
grabbing_spinner = false;
- grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position();
}
} else {
if (grabbing_spinner_attempt) {
@@ -283,6 +282,8 @@ void EditorSpinSlider::_notification(int p_what) {
Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE);
draw_rect(grabber_rect, c);
+ grabbing_spinner_mouse_pos = get_global_position() + grabber_rect.position + grabber_rect.size * 0.5;
+
bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner && !value_input_popup->is_visible();
if (grabber->is_visible() != display_grabber) {
if (display_grabber) {
diff --git a/editor/icons/CombineLines.svg b/editor/icons/CombineLines.svg
new file mode 100644
index 0000000000..124814ae88
--- /dev/null
+++ b/editor/icons/CombineLines.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M1 2v2h14V2zm7 5v2h7V7zm0 5v2h7v-2zM4.976 14V9h2l-1.5-2-1.5-2-1.5 2-1.5 2h2v5z" fill="#e0e0e0"/></svg>
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 4bb56beaeb..96002400f3 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1136,7 +1136,7 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito
return importer->import_animation(p_path, p_flags, p_bake_fps);
}
-void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache) {
+void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) {
EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
if (src_mesh_node) {
//is mesh
@@ -1216,7 +1216,28 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
n = n->get_parent_spatial();
}
- //use xf as transform for mesh, and bake it
+ Vector<uint8_t> lightmap_cache;
+ src_mesh_node->get_mesh()->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);
+
+ if (!lightmap_cache.is_empty()) {
+ if (r_lightmap_caches.is_empty()) {
+ r_lightmap_caches.push_back(lightmap_cache);
+ } else {
+ String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data
+
+ for (int i = 0; i < r_lightmap_caches.size(); i++) {
+ String md5 = String::md5(r_lightmap_caches[i].ptr());
+ if (new_md5 < md5) {
+ r_lightmap_caches.insert(i, lightmap_cache);
+ break;
+ }
+
+ if (new_md5 == md5) {
+ break;
+ }
+ }
+ }
+ }
}
if (save_to_file != String()) {
@@ -1265,7 +1286,7 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
for (int i = 0; i < p_node->get_child_count(); i++) {
- _generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_dst_lightmap_cache);
+ _generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_lightmap_caches);
}
}
@@ -1433,7 +1454,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
float lightmap_texel_size = MAX(0.001, texel_size);
Vector<uint8_t> src_lightmap_cache;
- Vector<uint8_t> dst_lightmap_cache;
+ Vector<Vector<uint8_t>> mesh_lightmap_caches;
{
src_lightmap_cache = FileAccess::get_file_as_array(p_source_file + ".unwrap_cache", &err);
@@ -1446,124 +1467,20 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
if (subresources.has("meshes")) {
mesh_data = subresources["meshes"];
}
- _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, dst_lightmap_cache);
+ _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);
- if (dst_lightmap_cache.size()) {
+ if (mesh_lightmap_caches.size()) {
FileAccessRef f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
if (f) {
- f->store_buffer(dst_lightmap_cache.ptr(), dst_lightmap_cache.size());
- }
- }
- err = OK;
-
-#if 0
- if (light_bake_mode == 2 /* || generate LOD */) {
- Map<Ref<ArrayMesh>, Transform> meshes;
- _find_meshes(scene, meshes);
-
- String file_id = src_path.get_file();
- String cache_file_path = base_path.plus_file(file_id + ".unwrap_cache");
-
- Vector<unsigned char> cache_data;
-
- if (FileAccess::exists(cache_file_path)) {
- Error err2;
- FileAccess *file = FileAccess::open(cache_file_path, FileAccess::READ, &err2);
-
- if (err2) {
- if (file) {
- memdelete(file);
- }
- } else {
- int cache_size = file->get_len();
- cache_data.resize(cache_size);
- file->get_buffer(cache_data.ptrw(), cache_size);
+ f->store_32(mesh_lightmap_caches.size());
+ for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
+ String md5 = String::md5(mesh_lightmap_caches[i].ptr());
+ f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
}
- }
-
- Map<String, unsigned int> used_unwraps;
-
- EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size());
- int step = 0;
- for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) {
- Ref<ArrayMesh> mesh = E->key();
- String name = mesh->get_name();
- if (name == "") { //should not happen but..
- name = "Mesh " + itos(step);
- }
-
- progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step);
-
- int *ret_cache_data = (int *)cache_data.ptrw();
- unsigned int ret_cache_size = cache_data.size();
- bool ret_used_cache = true; // Tell the unwrapper to use the cache
- Error err2 = mesh->lightmap_unwrap_cached(ret_cache_data, ret_cache_size, ret_used_cache, E->get(), texel_size);
-
- if (err2 != OK) {
- EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry.");
- } else {
-` String hash = String::md5((unsigned char *)ret_cache_data);
- used_unwraps.insert(hash, ret_cache_size);
-
- if (!ret_used_cache) {
- // Cache was not used, add the generated entry to the current cache
- if (cache_data.is_empty()) {
- cache_data.resize(4 + ret_cache_size);
- int *data = (int *)cache_data.ptrw();
- data[0] = 1;
- memcpy(&data[1], ret_cache_data, ret_cache_size);
- } else {
- int current_size = cache_data.size();
- cache_data.resize(cache_data.size() + ret_cache_size);
- unsigned char *ptrw = cache_data.ptrw();
- memcpy(&ptrw[current_size], ret_cache_data, ret_cache_size);
- int *data = (int *)ptrw;
- data[0] += 1;
- }
- }
- }
- step++;
- }
-
- Error err2;
- FileAccess *file = FileAccess::open(cache_file_path, FileAccess::WRITE, &err2);
-
- if (err2) {
- if (file) {
- memdelete(file);
- }
- } else {
- // Store number of entries
- file->store_32(used_unwraps.size());
-
- // Store cache entries
- const int *cache = (int *)cache_data.ptr();
- unsigned int r_idx = 1;
- for (int i = 0; i < cache[0]; ++i) {
- unsigned char *entry_start = (unsigned char *)&cache[r_idx];
- String entry_hash = String::md5(entry_start);
- if (used_unwraps.has(entry_hash)) {
- unsigned int entry_size = used_unwraps[entry_hash];
- file->store_buffer(entry_start, entry_size);
- }
-
- r_idx += 4; // hash
- r_idx += 2; // size hint
-
- int vertex_count = cache[r_idx];
- r_idx += 1; // vertex count
- r_idx += vertex_count; // vertex
- r_idx += vertex_count * 2; // uvs
-
- int index_count = cache[r_idx];
- r_idx += 1; // index count
- r_idx += index_count; // indices
- }
-
- file->close();
+ f->close();
}
}
-#endif
+ err = OK;
progress.step(TTR("Running Custom Script..."), 2);
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 00039f2ac6..8cb84abce2 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -119,7 +119,7 @@ class ResourceImporterScene : public ResourceImporter {
};
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
- void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache);
+ void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
void _add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes);
public:
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
index 28fdd4ddbd..bc7e8a1626 100644
--- a/editor/import/scene_importer_mesh.cpp
+++ b/editor/import/scene_importer_mesh.cpp
@@ -583,7 +583,7 @@ Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
return nm;
}
-extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
struct EditorSceneImporterMeshLightmapSurface {
Ref<Material> material;
@@ -593,22 +593,24 @@ struct EditorSceneImporterMeshLightmapSurface {
String name;
};
-Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
+Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
- Vector<float> vertices;
- Vector<float> normals;
- Vector<int> indices;
- Vector<float> uv;
- Vector<Pair<int, int>> uv_indices;
+ LocalVector<float> vertices;
+ LocalVector<float> normals;
+ LocalVector<int> indices;
+ LocalVector<float> uv;
+ LocalVector<Pair<int, int>> uv_indices;
Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
// Keep only the scale
- Transform transform = p_base_transform;
- transform.origin = Vector3();
- transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
+ Basis basis = p_base_transform.get_basis();
+ Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+ Transform transform;
+ transform.scale(scale);
Basis normal_basis = transform.basis.inverse().transposed();
@@ -623,15 +625,10 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
- Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+ PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
int vc = rvertices.size();
- const Vector3 *r = rvertices.ptr();
-
- Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
-
- ERR_FAIL_COND_V_MSG(rnormals.size() == 0, ERR_UNAVAILABLE, "Normals are required for lightmap unwrap.");
- const Vector3 *rn = rnormals.ptr();
+ PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
int vertex_ofs = vertices.size() / 3;
@@ -640,24 +637,29 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
uv_indices.resize(vertex_ofs + vc);
for (int j = 0; j < vc; j++) {
- Vector3 v = transform.xform(r[j]);
- Vector3 n = normal_basis.xform(rn[j]).normalized();
-
- vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
- vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
- vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
- normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
- normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
- normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
- uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
+ Vector3 v = transform.xform(rvertices[j]);
+ Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = n.x;
+ normals[(j + vertex_ofs) * 3 + 1] = n.y;
+ normals[(j + vertex_ofs) * 3 + 2] = n.z;
+ uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
}
- Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+ PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
int ic = rindices.size();
+ float eps = 1.19209290e-7F; // Taken from xatlas.h
if (ic == 0) {
for (int j = 0; j < vc / 3; j++) {
- if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
+ Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+ Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+ Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
continue;
}
@@ -667,15 +669,18 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
}
} else {
- const int *ri = rindices.ptr();
-
for (int j = 0; j < ic / 3; j++) {
- if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
+ Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+ Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+ Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
continue;
}
- indices.push_back(vertex_ofs + ri[j * 3 + 0]);
- indices.push_back(vertex_ofs + ri[j * 3 + 1]);
- indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+
+ indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
}
}
@@ -684,6 +689,9 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
//unwrap
+ bool use_cache = true; // Used to request cache generation and to know if cache was used
+ uint8_t *gen_cache;
+ int gen_cache_size;
float *gen_uvs;
int *gen_vertices;
int *gen_indices;
@@ -692,7 +700,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
int size_x;
int size_y;
- bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
if (!ok) {
return ERR_CANT_CREATE;
@@ -702,7 +710,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
clear();
//create surfacetools for each surface..
- Vector<Ref<SurfaceTool>> surfaces_tools;
+ LocalVector<Ref<SurfaceTool>> surfaces_tools;
for (int i = 0; i < lightmap_surfaces.size(); i++) {
Ref<SurfaceTool> st;
@@ -714,11 +722,12 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
}
print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
//go through all indices
for (int i = 0; i < gen_index_count; i += 3) {
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
- ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
@@ -728,49 +737,54 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsign
SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
- surfaces_tools.write[surface]->set_color(v.color);
+ surfaces_tools[surface]->set_color(v.color);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
- surfaces_tools.write[surface]->set_uv(v.uv);
+ surfaces_tools[surface]->set_uv(v.uv);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
- surfaces_tools.write[surface]->set_normal(v.normal);
+ surfaces_tools[surface]->set_normal(v.normal);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
Plane t;
t.normal = v.tangent;
t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
- surfaces_tools.write[surface]->set_tangent(t);
+ surfaces_tools[surface]->set_tangent(t);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
- surfaces_tools.write[surface]->set_bones(v.bones);
+ surfaces_tools[surface]->set_bones(v.bones);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
- surfaces_tools.write[surface]->set_weights(v.weights);
+ surfaces_tools[surface]->set_weights(v.weights);
}
Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
- surfaces_tools.write[surface]->set_uv2(uv2);
+ surfaces_tools[surface]->set_uv2(uv2);
- surfaces_tools.write[surface]->add_vertex(v.vertex);
+ surfaces_tools[surface]->add_vertex(v.vertex);
}
}
//generate surfaces
-
- for (int i = 0; i < surfaces_tools.size(); i++) {
- surfaces_tools.write[i]->index();
- Array arrays = surfaces_tools.write[i]->commit_to_arrays();
- add_surface(surfaces_tools.write[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools.write[i]->get_material(), surfaces_tools.write[i]->get_meta("name"));
+ for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ Array arrays = surfaces_tools[i]->commit_to_arrays();
+ add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name"));
}
set_lightmap_size_hint(Size2(size_x, size_y));
- if (!r_used_cache) {
- //free stuff
- ::free(gen_vertices);
- ::free(gen_indices);
- ::free(gen_uvs);
+ if (gen_cache_size > 0) {
+ r_dst_cache.resize(gen_cache_size);
+ memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+ memfree(gen_cache);
+ }
+
+ if (!use_cache) {
+ // Cache was not used, free the buffers
+ memfree(gen_vertices);
+ memfree(gen_indices);
+ memfree(gen_uvs);
}
return OK;
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
index 3326fab55d..b3e8137e0a 100644
--- a/editor/import/scene_importer_mesh.h
+++ b/editor/import/scene_importer_mesh.h
@@ -105,7 +105,7 @@ public:
Vector<Ref<Shape3D>> convex_decompose() const;
Ref<Shape3D> create_trimesh_shape() const;
Ref<NavigationMesh> create_navigation_mesh();
- Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size);
+ Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
void set_lightmap_size_hint(const Size2i &p_size);
Size2i get_lightmap_size_hint() const;
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index 3553450672..3b54b30b54 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
+#include "core/os/keyboard.h"
#include "editor/audio_stream_preview.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -144,23 +145,26 @@ void AudioStreamEditor::_draw_indicator() {
Rect2 rect = _preview->get_rect();
float len = stream->get_length();
float ofs_x = _current / len * rect.size.width;
- _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), get_theme_color("accent_color", "Editor"), 1);
+ const Color color = get_theme_color("accent_color", "Editor");
+ _indicator->draw_line(Point2(ofs_x, 0), Point2(ofs_x, rect.size.height), color, Math::round(2 * EDSCALE));
+ _indicator->draw_texture(
+ get_theme_icon("TimelineIndicator", "EditorIcons"),
+ Point2(ofs_x - get_theme_icon("TimelineIndicator", "EditorIcons")->get_width() * 0.5, 0),
+ color);
_current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /");
}
void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
- Ref<InputEventMouseButton> mb = p_event;
-
- if (mb.is_valid()) {
+ const Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (mb->is_pressed()) {
_seek_to(mb->get_position().x);
}
_dragging = mb->is_pressed();
}
- Ref<InputEventMouseMotion> mm = p_event;
-
+ const Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (_dragging) {
_seek_to(mm->get_position().x);
@@ -228,6 +232,7 @@ AudioStreamEditor::AudioStreamEditor() {
hbox->add_child(_play_button);
_play_button->set_focus_mode(Control::FOCUS_NONE);
_play_button->connect("pressed", callable_mp(this, &AudioStreamEditor::_play));
+ _play_button->set_shortcut(ED_SHORTCUT("inspector/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), KEY_SPACE));
_stop_button = memnew(Button);
_stop_button->set_flat(true);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 6ac47595dc..261621e10a 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1654,7 +1654,7 @@ bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEven
Ref<InputEventMouseButton> b = p_event;
// Open a sub-scene on double-click
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && b->is_doubleclick() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && b->is_double_click() && tool == TOOL_SELECT) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index bd721244ea..1d6ff92e0c 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -573,6 +573,7 @@ void TileMapEditor::_update_palette() {
entries2.sort_custom<SwapComparator>();
Ref<Texture2D> tex = tileset->tile_get_texture(sel_tile);
+ Color modulate = tileset->tile_get_modulate(sel_tile);
for (int i = 0; i < entries2.size(); i++) {
manual_palette->add_item(String());
@@ -588,6 +589,7 @@ void TileMapEditor::_update_palette() {
}
manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex);
+ manual_palette->set_item_icon_modulate(manual_palette->get_item_count() - 1, modulate);
}
manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries2[i]);
@@ -658,11 +660,15 @@ Vector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase,
}
// Check if the tile variation is the same
- Vector2 prev_position = node->get_cell_autotile_coord(p_start.x, p_start.y);
if (ids.size() == 1 && ids[0] == prev_id) {
int current = manual_palette->get_current();
- Vector2 position = manual_palette->get_item_metadata(current);
- if (prev_position == position) {
+ if (current == -1) {
+ // Same ID, no variation selected, nothing to change
+ return Vector<Vector2>();
+ }
+ Vector2 prev_autotile_coord = node->get_cell_autotile_coord(p_start.x, p_start.y);
+ Vector2 autotile_coord = manual_palette->get_item_metadata(current);
+ if (autotile_coord == prev_autotile_coord) {
// Same ID and variation, nothing to change
return Vector<Vector2>();
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index e51e8ee82e..2b75144ac7 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1762,7 +1762,7 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
emit_signal(SIGNAL_SELECTION_CHANGED);
- if (!mb->get_control() && mb->is_doubleclick()) {
+ if (!mb->get_control() && mb->is_double_click()) {
emit_signal(SIGNAL_PROJECT_ASK_OPEN);
}
}
@@ -2371,6 +2371,7 @@ void ProjectManager::_on_search_term_changed(const String &p_term) {
void ProjectManager::_bind_methods() {
ClassDB::bind_method("_unhandled_key_input", &ProjectManager::_unhandled_key_input);
ClassDB::bind_method("_update_project_buttons", &ProjectManager::_update_project_buttons);
+ ClassDB::bind_method("_version_button_pressed", &ProjectManager::_version_button_pressed);
}
void ProjectManager::_open_asset_library() {
@@ -2378,6 +2379,10 @@ void ProjectManager::_open_asset_library() {
tabs->set_current_tab(1);
}
+void ProjectManager::_version_button_pressed() {
+ DisplayServer::get_singleton()->clipboard_set(version_btn->get_text());
+}
+
ProjectManager::ProjectManager() {
// load settings
if (!EditorSettings::get_singleton()) {
@@ -2601,15 +2606,30 @@ ProjectManager::ProjectManager() {
settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN);
settings_hb->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT);
- Label *version_label = memnew(Label);
+ // A VBoxContainer that contains a dummy Control node to adjust the LinkButton's vertical position.
+ VBoxContainer *spacer_vb = memnew(VBoxContainer);
+ settings_hb->add_child(spacer_vb);
+
+ Control *v_spacer = memnew(Control);
+ spacer_vb->add_child(v_spacer);
+
+ version_btn = memnew(LinkButton);
String hash = String(VERSION_HASH);
if (hash.length() != 0) {
hash = "." + hash.left(9);
}
- version_label->set_text("v" VERSION_FULL_BUILD "" + hash);
- version_label->set_self_modulate(Color(1, 1, 1, 0.6));
- version_label->set_align(Label::ALIGN_CENTER);
- settings_hb->add_child(version_label);
+ version_btn->set_text("v" VERSION_FULL_BUILD + hash);
+ // Fade the version label to be less prominent, but still readable.
+ version_btn->set_self_modulate(Color(1, 1, 1, 0.6));
+ version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+ version_btn->set_tooltip(TTR("Click to copy."));
+ version_btn->connect("pressed", callable_mp(this, &ProjectManager::_version_button_pressed));
+ spacer_vb->add_child(version_btn);
+
+ // Add a small horizontal spacer between the version and language buttons
+ // to distinguish them.
+ Control *h_spacer = memnew(Control);
+ settings_hb->add_child(h_spacer);
language_btn = memnew(OptionButton);
language_btn->set_flat(true);
diff --git a/editor/project_manager.h b/editor/project_manager.h
index a66b7c4ab6..0fc69bd313 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -89,6 +89,7 @@ class ProjectManager : public Control {
ProjectDialog *npdialog;
OptionButton *language_btn;
+ LinkButton *version_btn;
void _open_asset_library();
void _scan_projects();
@@ -123,6 +124,7 @@ class ProjectManager : public Control {
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
void _files_dropped(PackedStringArray p_files, int p_screen);
+ void _version_button_pressed();
void _on_order_option_changed(int p_idx);
void _on_tab_changed(int p_tab);
void _on_search_term_changed(const String &p_term);
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index 81af4996ed..541ec224b9 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -270,16 +270,17 @@ void EditorSettingsDialog::_update_shortcuts() {
Array events; // Need to get the list of events into an array so it can be set as metadata on the item.
Vector<String> event_strings;
- List<Ref<InputEvent>> defaults = InputMap::get_singleton()->get_builtins().find(action_name).value();
- // Remove all non-key events from the defaults.
- for (List<Ref<InputEvent>>::Element *I = defaults.front(); I; I = I->next()) {
+ List<Ref<InputEvent>> all_default_events = InputMap::get_singleton()->get_builtins().find(action_name).value();
+ List<Ref<InputEventKey>> key_default_events;
+ // Remove all non-key events from the defaults. Only check keys, since we are in the editor.
+ for (List<Ref<InputEvent>>::Element *I = all_default_events.front(); I; I = I->next()) {
Ref<InputEventKey> k = I->get();
- if (k.is_null()) {
- I->erase();
+ if (k.is_valid()) {
+ key_default_events.push_back(k);
}
}
- bool same_as_defaults = defaults.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed.
+ bool same_as_defaults = key_default_events.size() == action.inputs.size(); // Initially this is set to just whether the arrays are equal. Later we check the events if needed.
int count = 0;
for (List<Ref<InputEvent>>::Element *I = action.inputs.front(); I; I = I->next()) {
@@ -288,12 +289,8 @@ void EditorSettingsDialog::_update_shortcuts() {
event_strings.push_back(I->get()->as_text());
// Only check if the events have been the same so far - once one fails, we don't need to check any more.
- if (same_as_defaults) {
- Ref<InputEventKey> k = defaults[count];
- // Only check keys, since we are in the editor.
- if (k.is_valid() && !defaults[count]->shortcut_match(I->get())) {
- same_as_defaults = false;
- }
+ if (same_as_defaults && !key_default_events[count]->shortcut_match(I->get())) {
+ same_as_defaults = false;
}
count++;
}