summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp931
-rw-r--r--editor/plugins/bone_map_editor_plugin.h65
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp89
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp10
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp12
-rw-r--r--editor/plugins/material_editor_plugin.cpp12
-rw-r--r--editor/plugins/material_editor_plugin.h4
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp18
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h6
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp40
-rw-r--r--editor/plugins/script_editor_plugin.cpp21
-rw-r--r--editor/plugins/script_editor_plugin.h10
-rw-r--r--editor/plugins/script_text_editor.cpp12
-rw-r--r--editor/plugins/script_text_editor.h2
-rw-r--r--editor/plugins/shader_editor_plugin.cpp117
-rw-r--r--editor/plugins/shader_editor_plugin.h8
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp1
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h1
-rw-r--r--editor/plugins/text_editor.cpp6
-rw-r--r--editor/plugins/text_editor.h2
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp30
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp40
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp13
26 files changed, 1257 insertions, 199 deletions
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index 70775c1ee2..5db9249af1 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -47,6 +47,9 @@ void BoneMapperButton::fetch_textures() {
set_offset(SIDE_TOP, 0);
set_offset(SIDE_BOTTOM, 0);
+ // Hack to avoid handle color darkening...
+ set_modulate(EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(4.25, 4.25, 4.25));
+
circle = memnew(TextureRect);
circle->set_texture(get_theme_icon(SNAME("BoneMapperHandleCircle"), SNAME("EditorIcons")));
add_child(circle);
@@ -98,14 +101,24 @@ BoneMapperButton::~BoneMapperButton() {
}
void BoneMapperItem::create_editor() {
- skeleton_bone_selector = memnew(EditorPropertyTextEnum);
- skeleton_bone_selector->setup(skeleton_bone_names, false, true);
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ add_child(hbox);
+
+ skeleton_bone_selector = memnew(EditorPropertyText);
skeleton_bone_selector->set_label(profile_bone_name);
skeleton_bone_selector->set_selectable(false);
+ skeleton_bone_selector->set_h_size_flags(SIZE_EXPAND_FILL);
skeleton_bone_selector->set_object_and_property(bone_map.ptr(), "bone_map/" + String(profile_bone_name));
skeleton_bone_selector->update_property();
skeleton_bone_selector->connect("property_changed", callable_mp(this, &BoneMapperItem::_value_changed));
- add_child(skeleton_bone_selector);
+ hbox->add_child(skeleton_bone_selector);
+
+ picker_button = memnew(Button);
+ picker_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons")));
+ picker_button->connect("pressed", callable_mp(this, &BoneMapperItem::_open_picker));
+ hbox->add_child(picker_button);
+
+ add_child(memnew(HSeparator));
}
void BoneMapperItem::_update_property() {
@@ -114,6 +127,10 @@ void BoneMapperItem::_update_property() {
}
}
+void BoneMapperItem::_open_picker() {
+ emit_signal(SNAME("pick"), profile_bone_name);
+}
+
void BoneMapperItem::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
bone_map->set(p_property, p_value);
}
@@ -133,25 +150,153 @@ void BoneMapperItem::_notification(int p_what) {
}
void BoneMapperItem::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("pick", PropertyInfo(Variant::STRING_NAME, "profile_bone_name")));
}
-BoneMapperItem::BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name) {
+BoneMapperItem::BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name) {
bone_map = p_bone_map;
- skeleton_bone_names = p_skeleton_bone_names;
profile_bone_name = p_profile_bone_name;
}
BoneMapperItem::~BoneMapperItem() {
}
+void BonePicker::create_editors() {
+ set_title(TTR("Bone Picker:"));
+
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ add_child(vbox);
+
+ bones = memnew(Tree);
+ bones->set_select_mode(Tree::SELECT_SINGLE);
+ bones->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ bones->set_hide_root(true);
+ bones->connect("item_activated", callable_mp(this, &BonePicker::_confirm));
+ vbox->add_child(bones);
+
+ create_bones_tree(skeleton);
+}
+
+void BonePicker::create_bones_tree(Skeleton3D *p_skeleton) {
+ bones->clear();
+
+ if (!p_skeleton) {
+ return;
+ }
+
+ TreeItem *root = bones->create_item();
+
+ HashMap<int, TreeItem *> items;
+
+ items.insert(-1, root);
+
+ Ref<Texture> bone_icon = get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons"));
+
+ Vector<int> bones_to_process = p_skeleton->get_parentless_bones();
+ bool is_first = true;
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ bones_to_process.erase(current_bone_idx);
+
+ Vector<int> current_bone_child_bones = p_skeleton->get_bone_children(current_bone_idx);
+ int child_bone_size = current_bone_child_bones.size();
+ for (int i = 0; i < child_bone_size; i++) {
+ bones_to_process.push_back(current_bone_child_bones[i]);
+ }
+
+ const int parent_idx = p_skeleton->get_bone_parent(current_bone_idx);
+ TreeItem *parent_item = items.find(parent_idx)->value;
+
+ TreeItem *joint_item = bones->create_item(parent_item);
+ items.insert(current_bone_idx, joint_item);
+
+ joint_item->set_text(0, p_skeleton->get_bone_name(current_bone_idx));
+ joint_item->set_icon(0, bone_icon);
+ joint_item->set_selectable(0, true);
+ joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));
+ if (is_first) {
+ is_first = false;
+ } else {
+ joint_item->set_collapsed(true);
+ }
+ }
+}
+
+void BonePicker::_confirm() {
+ _ok_pressed();
+}
+
+void BonePicker::popup_bones_tree(const Size2i &p_minsize) {
+ popup_centered(p_minsize);
+}
+
+bool BonePicker::has_selected_bone() {
+ TreeItem *selected = bones->get_selected();
+ if (!selected) {
+ return false;
+ }
+ return true;
+}
+
+StringName BonePicker::get_selected_bone() {
+ TreeItem *selected = bones->get_selected();
+ if (!selected) {
+ return StringName();
+ }
+ return selected->get_text(0);
+}
+
+void BonePicker::_bind_methods() {
+}
+
+void BonePicker::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ create_editors();
+ } break;
+ }
+}
+
+BonePicker::BonePicker(Skeleton3D *p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+BonePicker::~BonePicker() {
+}
+
void BoneMapper::create_editor() {
+ // Create Bone picker.
+ picker = memnew(BonePicker(skeleton));
+ picker->connect("confirmed", callable_mp(this, &BoneMapper::_apply_picker_selection));
+ add_child(picker, false, INTERNAL_MODE_FRONT);
+
+ profile_selector = memnew(EditorPropertyResource);
+ profile_selector->setup(bone_map.ptr(), "profile", "SkeletonProfile");
+ profile_selector->set_label("Profile");
+ profile_selector->set_selectable(false);
+ profile_selector->set_object_and_property(bone_map.ptr(), "profile");
+ profile_selector->update_property();
+ profile_selector->connect("property_changed", callable_mp(this, &BoneMapper::_profile_changed));
+ add_child(profile_selector);
+ add_child(memnew(HSeparator));
+
+ HBoxContainer *group_hbox = memnew(HBoxContainer);
+ add_child(group_hbox);
+
profile_group_selector = memnew(EditorPropertyEnum);
profile_group_selector->set_label("Group");
profile_group_selector->set_selectable(false);
+ profile_group_selector->set_h_size_flags(SIZE_EXPAND_FILL);
profile_group_selector->set_object_and_property(this, "current_group_idx");
profile_group_selector->update_property();
profile_group_selector->connect("property_changed", callable_mp(this, &BoneMapper::_value_changed));
- add_child(profile_group_selector);
+ group_hbox->add_child(profile_group_selector);
+
+ clear_mapping_button = memnew(Button);
+ clear_mapping_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")));
+ clear_mapping_button->set_tooltip(TTR("Clear mappings in current group."));
+ clear_mapping_button->connect("pressed", callable_mp(this, &BoneMapper::_clear_mapping_current_group));
+ group_hbox->add_child(clear_mapping_button);
bone_mapper_field = memnew(AspectRatioContainer);
bone_mapper_field->set_stretch_mode(AspectRatioContainer::STRETCH_FIT);
@@ -175,9 +320,6 @@ void BoneMapper::create_editor() {
mapper_item_vbox = memnew(VBoxContainer);
add_child(mapper_item_vbox);
- separator = memnew(HSeparator);
- add_child(separator);
-
recreate_items();
}
@@ -201,6 +343,18 @@ void BoneMapper::update_group_idx() {
}
}
+void BoneMapper::_pick_bone(const StringName &p_bone_name) {
+ picker_key_name = p_bone_name;
+ picker->popup_bones_tree(Size2(500, 500) * EDSCALE);
+}
+
+void BoneMapper::_apply_picker_selection() {
+ if (!picker->has_selected_bone()) {
+ return;
+ }
+ bone_map->set_skeleton_bone_name(picker_key_name, picker->get_selected_bone());
+}
+
void BoneMapper::set_current_group_idx(int p_group_idx) {
current_group_idx = p_group_idx;
recreate_editor();
@@ -282,6 +436,7 @@ void BoneMapper::clear_items() {
// Clear items.
int len = bone_mapper_items.size();
for (int i = 0; i < len; i++) {
+ bone_mapper_items[i]->disconnect("pick", callable_mp(this, &BoneMapper::_pick_bone));
mapper_item_vbox->remove_child(bone_mapper_items[i]);
memdelete(bone_mapper_items[i]);
}
@@ -293,16 +448,11 @@ void BoneMapper::recreate_items() {
// Create items by profile.
Ref<SkeletonProfile> profile = bone_map->get_profile();
if (profile.is_valid()) {
- PackedStringArray skeleton_bone_names;
- int len = skeleton->get_bone_count();
- for (int i = 0; i < len; i++) {
- skeleton_bone_names.push_back(skeleton->get_bone_name(i));
- }
-
- len = profile->get_bone_size();
+ int len = profile->get_bone_size();
for (int i = 0; i < len; i++) {
StringName bn = profile->get_bone_name(i);
- bone_mapper_items.append(memnew(BoneMapperItem(bone_map, skeleton_bone_names, bn)));
+ bone_mapper_items.append(memnew(BoneMapperItem(bone_map, bn)));
+ bone_mapper_items[i]->connect("pick", callable_mp(this, &BoneMapper::_pick_bone), CONNECT_DEFERRED);
mapper_item_vbox->add_child(bone_mapper_items[i]);
}
}
@@ -363,11 +513,754 @@ void BoneMapper::_update_state() {
}
}
+void BoneMapper::_clear_mapping_current_group() {
+ if (bone_map.is_valid()) {
+ Ref<SkeletonProfile> profile = bone_map->get_profile();
+ if (profile.is_valid() && profile->get_group_size() > 0) {
+ int len = profile->get_bone_size();
+ for (int i = 0; i < len; i++) {
+ if (profile->get_group(i) == profile->get_group_name(current_group_idx)) {
+ bone_map->_set_skeleton_bone_name(profile->get_bone_name(i), StringName());
+ }
+ }
+ recreate_items();
+ }
+ }
+}
+
+#ifdef MODULE_REGEX_ENABLED
+int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation, int p_parent, int p_child, int p_children_count) {
+ // There may be multiple candidates hit by existing the subsidiary bone.
+ // The one with the shortest name is probably the original.
+ LocalVector<String> hit_list;
+ String shortest = "";
+
+ for (int word_idx = 0; word_idx < p_picklist.size(); word_idx++) {
+ RegEx re = RegEx(p_picklist[word_idx]);
+ if (p_child == -1) {
+ Vector<int> bones_to_process = p_parent == -1 ? p_skeleton->get_parentless_bones() : p_skeleton->get_bone_children(p_parent);
+ while (bones_to_process.size() > 0) {
+ int idx = bones_to_process[0];
+ bones_to_process.erase(idx);
+ Vector<int> children = p_skeleton->get_bone_children(idx);
+ for (int i = 0; i < children.size(); i++) {
+ bones_to_process.push_back(children[i]);
+ }
+
+ if (p_children_count == 0 && children.size() > 0) {
+ continue;
+ }
+ if (p_children_count > 0 && children.size() < p_children_count) {
+ continue;
+ }
+
+ String bn = skeleton->get_bone_name(idx);
+ if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) {
+ hit_list.push_back(bn);
+ }
+ }
+
+ if (hit_list.size() > 0) {
+ shortest = hit_list[0];
+ for (uint32_t i = 0; i < hit_list.size(); i++) {
+ if (hit_list[i].length() < shortest.length()) {
+ shortest = hit_list[i]; // Prioritize parent.
+ }
+ }
+ }
+ } else {
+ int idx = skeleton->get_bone_parent(p_child);
+ while (idx != p_parent && idx >= 0) {
+ Vector<int> children = p_skeleton->get_bone_children(idx);
+ if (p_children_count == 0 && children.size() > 0) {
+ continue;
+ }
+ if (p_children_count > 0 && children.size() < p_children_count) {
+ continue;
+ }
+
+ String bn = skeleton->get_bone_name(idx);
+ if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) {
+ hit_list.push_back(bn);
+ }
+ idx = skeleton->get_bone_parent(idx);
+ }
+
+ if (hit_list.size() > 0) {
+ shortest = hit_list[0];
+ for (uint32_t i = 0; i < hit_list.size(); i++) {
+ if (hit_list[i].length() <= shortest.length()) {
+ shortest = hit_list[i]; // Prioritize parent.
+ }
+ }
+ }
+ }
+
+ if (shortest != "") {
+ break;
+ }
+ }
+
+ if (shortest == "") {
+ return -1;
+ }
+
+ return skeleton->find_bone(shortest);
+}
+
+BoneMapper::BoneSegregation BoneMapper::guess_bone_segregation(String p_bone_name) {
+ String fixed_bn = p_bone_name.camelcase_to_underscore().to_lower();
+
+ LocalVector<String> left_words;
+ left_words.push_back("(?<![a-zA-Z])left");
+ left_words.push_back("(?<![a-zA-Z0-9])l(?![a-zA-Z0-9])");
+
+ LocalVector<String> right_words;
+ right_words.push_back("(?<![a-zA-Z])right");
+ right_words.push_back("(?<![a-zA-Z0-9])r(?![a-zA-Z0-9])");
+
+ for (uint32_t i = 0; i < left_words.size(); i++) {
+ RegEx re_l = RegEx(left_words[i]);
+ if (!re_l.search(fixed_bn).is_null()) {
+ return BONE_SEGREGATION_LEFT;
+ }
+ RegEx re_r = RegEx(right_words[i]);
+ if (!re_r.search(fixed_bn).is_null()) {
+ return BONE_SEGREGATION_RIGHT;
+ }
+ }
+
+ return BONE_SEGREGATION_NONE;
+}
+
+void BoneMapper::_run_auto_mapping() {
+ auto_mapping_process(bone_map);
+ recreate_items();
+}
+
+void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
+ WARN_PRINT("Run auto mapping.");
+
+ int bone_idx = -1;
+ Vector<String> picklist; // Use Vector<String> because match words have priority.
+ Vector<int> search_path;
+
+ // 1. Guess Hips
+ picklist.push_back("hip");
+ picklist.push_back("pelvis");
+ picklist.push_back("waist");
+ picklist.push_back("torso");
+ int hips = search_bone_by_name(skeleton, picklist);
+ if (hips == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess Hips. Abort auto mapping.");
+ return; // If there is no Hips, we cannot guess bone after then.
+ } else {
+ p_bone_map->_set_skeleton_bone_name("Hips", skeleton->get_bone_name(hips));
+ }
+ picklist.clear();
+
+ // 2. Guess Root
+ bone_idx = skeleton->get_bone_parent(hips);
+ while (bone_idx >= 0) {
+ search_path.push_back(bone_idx);
+ bone_idx = skeleton->get_bone_parent(bone_idx);
+ }
+ if (search_path.size() == 0) {
+ bone_idx = -1;
+ } else if (search_path.size() == 1) {
+ bone_idx = search_path[0]; // It is only one bone which can be root.
+ } else {
+ bool found = false;
+ for (int i = 0; i < search_path.size(); i++) {
+ RegEx re = RegEx("root");
+ if (!re.search(skeleton->get_bone_name(search_path[i]).to_lower()).is_null()) {
+ bone_idx = search_path[i]; // Name match is preferred.
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ for (int i = 0; i < search_path.size(); i++) {
+ if (Vector3(0, 0, 0).is_equal_approx(skeleton->get_bone_global_rest(search_path[i]).origin)) {
+ bone_idx = search_path[i]; // The bone existing at the origin is appropriate as a root.
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ bone_idx = search_path[search_path.size() - 1]; // Ambiguous, but most parental bone selected.
+ }
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess Root."); // Root is not required, so continue.
+ } else {
+ p_bone_map->_set_skeleton_bone_name("Root", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ search_path.clear();
+
+ // 3. Guess Neck
+ picklist.push_back("neck");
+ picklist.push_back("head"); // For no neck model.
+ picklist.push_back("face"); // Same above.
+ int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips);
+ picklist.clear();
+
+ // 4. Guess Head
+ picklist.push_back("head");
+ picklist.push_back("face");
+ int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck);
+ if (head == -1) {
+ search_path = skeleton->get_bone_children(neck);
+ if (search_path.size() == 1) {
+ head = search_path[0]; // Maybe only one child of the Neck is Head.
+ }
+ }
+ if (head == -1) {
+ if (neck != -1) {
+ head = neck; // The head animation should have more movement.
+ neck = -1;
+ p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
+ } else {
+ WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step.
+ }
+ } else {
+ p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck));
+ p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head));
+ }
+ picklist.clear();
+ search_path.clear();
+
+ int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1);
+ if (neck_or_head != -1) {
+ // 4-1. Guess Eyes
+ picklist.push_back("eye(?!.*(brow|lash|lid))");
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head);
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftEye.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head);
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightEye.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ picklist.clear();
+
+ // 4-2. Guess Jaw
+ picklist.push_back("jaw");
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head);
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess Jaw.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ picklist.clear();
+ }
+
+ // 5. Guess Foots
+ picklist.push_back("foot");
+ picklist.push_back("ankle");
+ int left_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips);
+ if (left_foot == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftFoot.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftFoot", skeleton->get_bone_name(left_foot));
+ }
+ int right_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips);
+ if (right_foot == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightFoot.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightFoot", skeleton->get_bone_name(right_foot));
+ }
+ picklist.clear();
+
+ // 5-1. Guess LowerLegs
+ picklist.push_back("(low|under).*leg");
+ picklist.push_back("knee");
+ picklist.push_back("shin");
+ picklist.push_back("calf");
+ picklist.push_back("leg");
+ int left_lower_leg = -1;
+ if (left_foot != -1) {
+ left_lower_leg = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_foot);
+ }
+ if (left_lower_leg == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftLowerLeg.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftLowerLeg", skeleton->get_bone_name(left_lower_leg));
+ }
+ int right_lower_leg = -1;
+ if (right_foot != -1) {
+ right_lower_leg = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_foot);
+ }
+ if (right_lower_leg == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightLowerLeg.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightLowerLeg", skeleton->get_bone_name(right_lower_leg));
+ }
+ picklist.clear();
+
+ // 5-2. Guess UpperLegs
+ picklist.push_back("up.*leg");
+ picklist.push_back("thigh");
+ picklist.push_back("leg");
+ if (left_lower_leg != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_lower_leg);
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftUpperLeg.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftUpperLeg", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ if (right_lower_leg != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_lower_leg);
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightUpperLeg.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightUpperLeg", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ picklist.clear();
+
+ // 5-3. Guess Toes
+ picklist.push_back("toe");
+ picklist.push_back("ball");
+ if (left_foot != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_foot);
+ if (bone_idx == -1) {
+ search_path = skeleton->get_bone_children(left_foot);
+ if (search_path.size() == 1) {
+ bone_idx = search_path[0]; // Maybe only one child of the Foot is Toes.
+ }
+ search_path.clear();
+ }
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftToes.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftToes", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ if (right_foot != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_foot);
+ if (bone_idx == -1) {
+ search_path = skeleton->get_bone_children(right_foot);
+ if (search_path.size() == 1) {
+ bone_idx = search_path[0]; // Maybe only one child of the Foot is Toes.
+ }
+ search_path.clear();
+ }
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightToes.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightToes", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ picklist.clear();
+
+ // 6. Guess Hands
+ picklist.push_back("hand");
+ picklist.push_back("wrist");
+ picklist.push_back("palm");
+ picklist.push_back("fingers");
+ int left_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, -1, 5);
+ if (left_hand_or_palm == -1) {
+ // Ambiguous, but try again for fewer finger models.
+ left_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips);
+ }
+ int left_hand = left_hand_or_palm; // Check for the presence of a wrist, since bones with five children may be palmar.
+ while (left_hand != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_hand);
+ if (bone_idx == -1) {
+ break;
+ }
+ left_hand = bone_idx;
+ }
+ if (left_hand == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftHand.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftHand", skeleton->get_bone_name(left_hand));
+ }
+ bone_idx = -1;
+ int right_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, -1, 5);
+ if (right_hand_or_palm == -1) {
+ // Ambiguous, but try again for fewer finger models.
+ right_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips);
+ }
+ int right_hand = right_hand_or_palm;
+ while (right_hand != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_hand);
+ if (bone_idx == -1) {
+ break;
+ }
+ right_hand = bone_idx;
+ }
+ if (right_hand == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightHand.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightHand", skeleton->get_bone_name(right_hand));
+ }
+ bone_idx = -1;
+ picklist.clear();
+
+ // 6-1. Guess Finger
+ bool named_finger_is_found = false;
+ LocalVector<String> fingers;
+ fingers.push_back("thumb|pollex");
+ fingers.push_back("index|fore");
+ fingers.push_back("middle");
+ fingers.push_back("ring");
+ fingers.push_back("little|pinkie|pinky");
+ if (left_hand_or_palm != -1) {
+ LocalVector<LocalVector<String>> left_fingers_map;
+ left_fingers_map.resize(5);
+ left_fingers_map[0].push_back("LeftThumbMetacarpal");
+ left_fingers_map[0].push_back("LeftThumbProximal");
+ left_fingers_map[0].push_back("LeftThumbDistal");
+ left_fingers_map[1].push_back("LeftIndexProximal");
+ left_fingers_map[1].push_back("LeftIndexIntermediate");
+ left_fingers_map[1].push_back("LeftIndexDistal");
+ left_fingers_map[2].push_back("LeftMiddleProximal");
+ left_fingers_map[2].push_back("LeftMiddleIntermediate");
+ left_fingers_map[2].push_back("LeftMiddleDistal");
+ left_fingers_map[3].push_back("LeftRingProximal");
+ left_fingers_map[3].push_back("LeftRingIntermediate");
+ left_fingers_map[3].push_back("LeftRingDistal");
+ left_fingers_map[4].push_back("LeftLittleProximal");
+ left_fingers_map[4].push_back("LeftLittleIntermediate");
+ left_fingers_map[4].push_back("LeftLittleDistal");
+ for (int i = 0; i < 5; i++) {
+ picklist.push_back(fingers[i]);
+ int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_hand_or_palm, -1, 0);
+ if (finger != -1) {
+ while (finger != left_hand_or_palm && finger >= 0) {
+ search_path.push_back(finger);
+ finger = skeleton->get_bone_parent(finger);
+ }
+ search_path.reverse();
+ if (search_path.size() == 1) {
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ named_finger_is_found = true;
+ } else if (search_path.size() == 2) {
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ named_finger_is_found = true;
+ } else if (search_path.size() >= 3) {
+ search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone.
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][2], skeleton->get_bone_name(search_path[2]));
+ named_finger_is_found = true;
+ }
+ }
+ picklist.clear();
+ search_path.clear();
+ }
+
+ // It is a bit corner case, but possibly the finger names are sequentially numbered...
+ if (!named_finger_is_found) {
+ picklist.push_back("finger");
+ RegEx finger_re = RegEx("finger");
+ search_path = skeleton->get_bone_children(left_hand_or_palm);
+ Vector<String> finger_names;
+ for (int i = 0; i < search_path.size(); i++) {
+ String bn = skeleton->get_bone_name(search_path[i]);
+ if (!finger_re.search(bn.to_lower()).is_null()) {
+ finger_names.push_back(bn);
+ }
+ }
+ finger_names.sort(); // Order by lexicographic, normal use cases never have more than 10 fingers in one hand.
+ search_path.clear();
+ for (int i = 0; i < finger_names.size(); i++) {
+ if (i >= 5) {
+ break;
+ }
+ int finger_root = skeleton->find_bone(finger_names[i]);
+ int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, finger_root, -1, 0);
+ if (finger != -1) {
+ while (finger != finger_root && finger >= 0) {
+ search_path.push_back(finger);
+ finger = skeleton->get_bone_parent(finger);
+ }
+ }
+ search_path.push_back(finger_root);
+ search_path.reverse();
+ if (search_path.size() == 1) {
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ } else if (search_path.size() == 2) {
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ } else if (search_path.size() >= 3) {
+ search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone.
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][2], skeleton->get_bone_name(search_path[2]));
+ }
+ search_path.clear();
+ }
+ picklist.clear();
+ }
+ }
+ named_finger_is_found = false;
+ if (right_hand_or_palm != -1) {
+ LocalVector<LocalVector<String>> right_fingers_map;
+ right_fingers_map.resize(5);
+ right_fingers_map[0].push_back("RightThumbMetacarpal");
+ right_fingers_map[0].push_back("RightThumbProximal");
+ right_fingers_map[0].push_back("RightThumbDistal");
+ right_fingers_map[1].push_back("RightIndexProximal");
+ right_fingers_map[1].push_back("RightIndexIntermediate");
+ right_fingers_map[1].push_back("RightIndexDistal");
+ right_fingers_map[2].push_back("RightMiddleProximal");
+ right_fingers_map[2].push_back("RightMiddleIntermediate");
+ right_fingers_map[2].push_back("RightMiddleDistal");
+ right_fingers_map[3].push_back("RightRingProximal");
+ right_fingers_map[3].push_back("RightRingIntermediate");
+ right_fingers_map[3].push_back("RightRingDistal");
+ right_fingers_map[4].push_back("RightLittleProximal");
+ right_fingers_map[4].push_back("RightLittleIntermediate");
+ right_fingers_map[4].push_back("RightLittleDistal");
+ for (int i = 0; i < 5; i++) {
+ picklist.push_back(fingers[i]);
+ int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_hand_or_palm, -1, 0);
+ if (finger != -1) {
+ while (finger != right_hand_or_palm && finger >= 0) {
+ search_path.push_back(finger);
+ finger = skeleton->get_bone_parent(finger);
+ }
+ search_path.reverse();
+ if (search_path.size() == 1) {
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ named_finger_is_found = true;
+ } else if (search_path.size() == 2) {
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ named_finger_is_found = true;
+ } else if (search_path.size() >= 3) {
+ search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone.
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][2], skeleton->get_bone_name(search_path[2]));
+ named_finger_is_found = true;
+ }
+ }
+ picklist.clear();
+ search_path.clear();
+ }
+
+ // It is a bit corner case, but possibly the finger names are sequentially numbered...
+ if (!named_finger_is_found) {
+ picklist.push_back("finger");
+ RegEx finger_re = RegEx("finger");
+ search_path = skeleton->get_bone_children(right_hand_or_palm);
+ Vector<String> finger_names;
+ for (int i = 0; i < search_path.size(); i++) {
+ String bn = skeleton->get_bone_name(search_path[i]);
+ if (!finger_re.search(bn.to_lower()).is_null()) {
+ finger_names.push_back(bn);
+ }
+ }
+ finger_names.sort(); // Order by lexicographic, normal use cases never have more than 10 fingers in one hand.
+ search_path.clear();
+ for (int i = 0; i < finger_names.size(); i++) {
+ if (i >= 5) {
+ break;
+ }
+ int finger_root = skeleton->find_bone(finger_names[i]);
+ int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, finger_root, -1, 0);
+ if (finger != -1) {
+ while (finger != finger_root && finger >= 0) {
+ search_path.push_back(finger);
+ finger = skeleton->get_bone_parent(finger);
+ }
+ }
+ search_path.push_back(finger_root);
+ search_path.reverse();
+ if (search_path.size() == 1) {
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ } else if (search_path.size() == 2) {
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ } else if (search_path.size() >= 3) {
+ search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone.
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1]));
+ p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][2], skeleton->get_bone_name(search_path[2]));
+ }
+ search_path.clear();
+ }
+ picklist.clear();
+ }
+ }
+
+ // 7. Guess Arms
+ picklist.push_back("shoulder");
+ picklist.push_back("clavicle");
+ picklist.push_back("collar");
+ int left_shoulder = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips);
+ if (left_shoulder == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftShoulder.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftShoulder", skeleton->get_bone_name(left_shoulder));
+ }
+ int right_shoulder = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips);
+ if (right_shoulder == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightShoulder.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightShoulder", skeleton->get_bone_name(right_shoulder));
+ }
+ picklist.clear();
+
+ // 7-1. Guess LowerArms
+ picklist.push_back("(low|fore).*arm");
+ picklist.push_back("elbow");
+ picklist.push_back("arm");
+ int left_lower_arm = -1;
+ if (left_shoulder != -1 && left_hand_or_palm != -1) {
+ left_lower_arm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_shoulder, left_hand_or_palm);
+ }
+ if (left_lower_arm == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftLowerArm.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftLowerArm", skeleton->get_bone_name(left_lower_arm));
+ }
+ int right_lower_arm = -1;
+ if (right_shoulder != -1 && right_hand_or_palm != -1) {
+ right_lower_arm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_shoulder, right_hand_or_palm);
+ }
+ if (right_lower_arm == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightLowerArm.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightLowerArm", skeleton->get_bone_name(right_lower_arm));
+ }
+ picklist.clear();
+
+ // 7-2. Guess UpperArms
+ picklist.push_back("up.*arm");
+ picklist.push_back("arm");
+ if (left_shoulder != -1 && left_lower_arm != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_shoulder, left_lower_arm);
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess LeftUpperArm.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("LeftUpperArm", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ if (right_shoulder != -1 && right_lower_arm != -1) {
+ bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_shoulder, right_lower_arm);
+ }
+ if (bone_idx == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess RightUpperArm.");
+ } else {
+ p_bone_map->_set_skeleton_bone_name("RightUpperArm", skeleton->get_bone_name(bone_idx));
+ }
+ bone_idx = -1;
+ picklist.clear();
+
+ // 8. Guess UpperChest or Chest
+ if (neck_or_head == -1) {
+ return; // Abort.
+ }
+ int chest_or_upper_chest = skeleton->get_bone_parent(neck_or_head);
+ bool is_appropriate = true;
+ if (left_shoulder != -1) {
+ bone_idx = skeleton->get_bone_parent(left_shoulder);
+ bool detect = false;
+ while (bone_idx != hips && bone_idx >= 0) {
+ if (bone_idx == chest_or_upper_chest) {
+ detect = true;
+ break;
+ }
+ bone_idx = skeleton->get_bone_parent(bone_idx);
+ }
+ if (!detect) {
+ is_appropriate = false;
+ }
+ bone_idx = -1;
+ }
+ if (right_shoulder != -1) {
+ bone_idx = skeleton->get_bone_parent(right_shoulder);
+ bool detect = false;
+ while (bone_idx != hips && bone_idx >= 0) {
+ if (bone_idx == chest_or_upper_chest) {
+ detect = true;
+ break;
+ }
+ bone_idx = skeleton->get_bone_parent(bone_idx);
+ }
+ if (!detect) {
+ is_appropriate = false;
+ }
+ bone_idx = -1;
+ }
+ if (!is_appropriate) {
+ if (skeleton->get_bone_parent(left_shoulder) == skeleton->get_bone_parent(right_shoulder)) {
+ chest_or_upper_chest = skeleton->get_bone_parent(left_shoulder);
+ } else {
+ chest_or_upper_chest = -1;
+ }
+ }
+ if (chest_or_upper_chest == -1) {
+ WARN_PRINT("Auto Mapping couldn't guess Chest or UpperChest. Abort auto mapping.");
+ return; // Will be not able to guess Spines.
+ }
+
+ // 9. Guess Spines
+ bone_idx = skeleton->get_bone_parent(chest_or_upper_chest);
+ while (bone_idx != hips && bone_idx >= 0) {
+ search_path.push_back(bone_idx);
+ bone_idx = skeleton->get_bone_parent(bone_idx);
+ }
+ search_path.reverse();
+ if (search_path.size() == 0) {
+ p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(chest_or_upper_chest)); // Maybe chibi model...?
+ } else if (search_path.size() == 1) {
+ p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name("Chest", skeleton->get_bone_name(chest_or_upper_chest));
+ } else if (search_path.size() >= 2) {
+ p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(search_path[0]));
+ p_bone_map->_set_skeleton_bone_name("Chest", skeleton->get_bone_name(search_path[search_path.size() - 1])); // Probably UppeChest's parent is appropriate.
+ p_bone_map->_set_skeleton_bone_name("UpperChest", skeleton->get_bone_name(chest_or_upper_chest));
+ }
+ bone_idx = -1;
+ search_path.clear();
+
+ WARN_PRINT("Finish auto mapping.");
+}
+#endif // MODULE_REGEX_ENABLED
+
void BoneMapper::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
set(p_property, p_value);
recreate_editor();
}
+void BoneMapper::_profile_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
+ bone_map->set(p_property, p_value);
+
+ // Run auto mapping when setting SkeletonProfileHumanoid by GUI Editor.
+ Ref<SkeletonProfile> profile = bone_map->get_profile();
+ if (profile.is_valid()) {
+ SkeletonProfileHumanoid *hmn = Object::cast_to<SkeletonProfileHumanoid>(profile.ptr());
+ if (hmn) {
+#ifdef MODULE_REGEX_ENABLED
+ _run_auto_mapping();
+#endif // MODULE_REGEX_ENABLED
+ }
+ }
+}
+
void BoneMapper::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_current_group_idx", "current_group_idx"), &BoneMapper::set_current_group_idx);
ClassDB::bind_method(D_METHOD("get_current_group_idx"), &BoneMapper::get_current_group_idx);
@@ -444,10 +1337,6 @@ void BoneMapEditor::_notification(int p_what) {
create_editors();
} break;
case NOTIFICATION_EXIT_TREE: {
- if (bone_mapper) {
- remove_child(bone_mapper);
- bone_mapper->queue_delete();
- }
skeleton = nullptr;
} break;
}
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
index 339547ea10..0541ce6eac 100644
--- a/editor/plugins/bone_map_editor_plugin.h
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -34,6 +34,12 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
+
+#include "modules/modules_enabled.gen.h" // For regex.
+#ifdef MODULE_REGEX_ENABLED
+#include "modules/regex/regex.h"
+#endif
+
#include "scene/3d/skeleton_3d.h"
#include "scene/gui/color_rect.h"
#include "scene/gui/dialogs.h"
@@ -79,12 +85,13 @@ class BoneMapperItem : public VBoxContainer {
int button_id = -1;
StringName profile_bone_name;
- PackedStringArray skeleton_bone_names;
Ref<BoneMap> bone_map;
- EditorPropertyTextEnum *skeleton_bone_selector;
+ EditorPropertyText *skeleton_bone_selector;
+ Button *picker_button;
void _update_property();
+ void _open_picker();
protected:
void _notification(int p_what);
@@ -95,20 +102,49 @@ protected:
public:
void assign_button_id(int p_button_id);
- BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name = StringName());
+ BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name = StringName());
~BoneMapperItem();
};
+class BonePicker : public AcceptDialog {
+ GDCLASS(BonePicker, AcceptDialog);
+
+ Skeleton3D *skeleton = nullptr;
+ Tree *bones = nullptr;
+
+public:
+ void popup_bones_tree(const Size2i &p_minsize = Size2i());
+ bool has_selected_bone();
+ StringName get_selected_bone();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ void _confirm();
+
+private:
+ void create_editors();
+ void create_bones_tree(Skeleton3D *p_skeleton);
+
+public:
+ BonePicker(Skeleton3D *p_skeleton);
+ ~BonePicker();
+};
+
class BoneMapper : public VBoxContainer {
GDCLASS(BoneMapper, VBoxContainer);
Skeleton3D *skeleton;
Ref<BoneMap> bone_map;
+ EditorPropertyResource *profile_selector;
+
Vector<BoneMapperItem *> bone_mapper_items;
+ Button *clear_mapping_button;
+
VBoxContainer *mapper_item_vbox;
- HSeparator *separator;
int current_group_idx = 0;
int current_bone_idx = -1;
@@ -126,10 +162,31 @@ class BoneMapper : public VBoxContainer {
void update_group_idx();
void _update_state();
+ /* Bone picker */
+ BonePicker *picker = nullptr;
+ StringName picker_key_name;
+ void _pick_bone(const StringName &p_bone_name);
+ void _apply_picker_selection();
+ void _clear_mapping_current_group();
+
+#ifdef MODULE_REGEX_ENABLED
+ /* For auto mapping */
+ enum BoneSegregation {
+ BONE_SEGREGATION_NONE,
+ BONE_SEGREGATION_LEFT,
+ BONE_SEGREGATION_RIGHT
+ };
+ int search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1);
+ BoneSegregation guess_bone_segregation(String p_bone_name);
+ void auto_mapping_process(Ref<BoneMap> &p_bone_map);
+ void _run_auto_mapping();
+#endif // MODULE_REGEX_ENABLED
+
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
+ virtual void _profile_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
public:
void set_current_group_idx(int p_group_idx);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 5682df845e..17c2d26dc2 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -2958,6 +2958,9 @@ void CanvasItemEditor::_draw_ruler_tool() {
Point2 corner = Point2(begin.x, end.y);
Vector2 length_vector = (begin - end).abs() / zoom;
+ const real_t horizontal_angle_rad = length_vector.angle();
+ const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad;
+
Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
@@ -2974,6 +2977,42 @@ void CanvasItemEditor::_draw_ruler_tool() {
text_pos.x = CLAMP(text_pos.x, text_width / 2, viewport->get_rect().size.x - text_width * 1.5);
text_pos.y = CLAMP(text_pos.y, text_height * 1.5, viewport->get_rect().size.y - text_height * 1.5);
+ // Draw lines.
+ viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3));
+
+ bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x));
+ if (draw_secondary_lines) {
+ viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE));
+ viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE));
+
+ // Angle arcs.
+ int arc_point_count = 8;
+ real_t arc_radius_max_length_percent = 0.1;
+ real_t ruler_length = length_vector.length() * zoom;
+ real_t arc_max_radius = 50.0;
+ real_t arc_line_width = 2.0;
+
+ const Vector2 end_to_begin = (end - begin);
+
+ real_t arc_1_start_angle = end_to_begin.x < 0
+ ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0)
+ : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad);
+ real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad;
+ // Constrain arc to triangle height & max size.
+ real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius);
+
+ real_t arc_2_start_angle = end_to_begin.x < 0
+ ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad)
+ : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI);
+ real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad;
+ // Constrain arc to triangle width & max size.
+ real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius);
+
+ viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width));
+ viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width));
+ }
+
+ // Draw text.
if (begin.is_equal_approx(end)) {
viewport->draw_string_outline(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
viewport->draw_string(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
@@ -2985,17 +3024,7 @@ void CanvasItemEditor::_draw_ruler_tool() {
viewport->draw_string_outline(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
- bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x));
-
- viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3));
if (draw_secondary_lines) {
- viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE));
- viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE));
- }
-
- if (draw_secondary_lines) {
- const real_t horizontal_angle_rad = length_vector.angle();
- const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad;
const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI);
const int vertical_angle = round(180 * vertical_angle_rad / Math_PI);
@@ -3032,32 +3061,6 @@ void CanvasItemEditor::_draw_ruler_tool() {
}
viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color);
viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color);
-
- // Angle arcs
- int arc_point_count = 8;
- real_t arc_radius_max_length_percent = 0.1;
- real_t ruler_length = length_vector.length() * zoom;
- real_t arc_max_radius = 50.0;
- real_t arc_line_width = 2.0;
-
- const Vector2 end_to_begin = (end - begin);
-
- real_t arc_1_start_angle = end_to_begin.x < 0
- ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0)
- : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad);
- real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad;
- // Constrain arc to triangle height & max size
- real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius);
-
- real_t arc_2_start_angle = end_to_begin.x < 0
- ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad)
- : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI);
- real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad;
- // Constrain arc to triangle width & max size
- real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius);
-
- viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width));
- viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width));
}
if (grid_snap_active) {
@@ -5560,19 +5563,7 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe
void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) {
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
String name = path.get_file().get_basename();
- switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) {
- case NAME_CASING_PASCAL_CASE:
- name = name.capitalize().replace(" ", "");
- break;
- case NAME_CASING_CAMEL_CASE:
- name = name.capitalize().replace(" ", "");
- name[0] = name.to_lower()[0];
- break;
- case NAME_CASING_SNAKE_CASE:
- name = name.capitalize().replace(" ", "_").to_lower();
- break;
- }
- child->set_name(name);
+ child->set_name(Node::adjust_name_casing(name));
Ref<Texture2D> texture = ResourceCache::get_ref(path);
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index e20d298195..e56fd5dfe3 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -37,7 +37,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/gui/separator.h"
-#include "scene/resources/particles_material.h"
+#include "scene/resources/particle_process_material.h"
void CPUParticles2DEditorPlugin::edit(Object *p_object) {
particles = Object::cast_to<CPUParticles2D>(p_object);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 1487f8b7bc..e2d19c34e6 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -38,7 +38,7 @@
#include "editor/scene_tree_dock.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/gui/separator.h"
-#include "scene/resources/particles_material.h"
+#include "scene/resources/particle_process_material.h"
void GPUParticles2DEditorPlugin::edit(Object *p_object) {
particles = Object::cast_to<GPUParticles2D>(p_object);
@@ -167,9 +167,9 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() {
}
void GPUParticles2DEditorPlugin::_generate_emission_mask() {
- Ref<ParticlesMaterial> pm = particles->get_process_material();
+ Ref<ParticleProcessMaterial> pm = particles->get_process_material();
if (!pm.is_valid()) {
- EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material"));
+ EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticleProcessMaterial process material"));
return;
}
@@ -320,7 +320,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() {
}
if (valid_normals.size()) {
- pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
+ pm->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
Vector<uint8_t> normdata;
normdata.resize(w * h * 2 * sizeof(float)); //use RG texture
@@ -339,7 +339,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() {
pm->set_emission_normal_texture(ImageTexture::create_from_image(img));
} else {
- pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS);
+ pm->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_POINTS);
}
}
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 335efd6949..ebc92bf531 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -36,7 +36,7 @@
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/scene_tree_dock.h"
#include "scene/3d/cpu_particles_3d.h"
-#include "scene/resources/particles_material.h"
+#include "scene/resources/particle_process_material.h"
bool GPUParticles3DEditorBase::_generate(Vector<Vector3> &points, Vector<Vector3> &normals) {
bool use_normals = emission_fill->get_selected() == 1;
@@ -255,9 +255,9 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
}
} break;
case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: {
- Ref<ParticlesMaterial> material = node->get_process_material();
+ Ref<ParticleProcessMaterial> material = node->get_process_material();
if (material.is_null()) {
- EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required."));
+ EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticleProcessMaterial' is required."));
return;
}
@@ -366,11 +366,11 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<Image> image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img));
Ref<ImageTexture> tex = ImageTexture::create_from_image(image);
- Ref<ParticlesMaterial> material = node->get_process_material();
+ Ref<ParticleProcessMaterial> material = node->get_process_material();
ERR_FAIL_COND(material.is_null());
if (normals.size() > 0) {
- material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
+ material->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
material->set_emission_point_count(point_count);
material->set_emission_point_texture(tex);
@@ -392,7 +392,7 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<Image> image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2));
material->set_emission_normal_texture(ImageTexture::create_from_image(image2));
} else {
- material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS);
+ material->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_POINTS);
material->set_emission_point_count(point_count);
material->set_emission_point_texture(tex);
}
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 5d59f62f05..9fcb6619c0 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -36,7 +36,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "scene/gui/subviewport_container.h"
#include "scene/resources/fog_material.h"
-#include "scene/resources/particles_material.h"
+#include "scene/resources/particle_process_material.h"
#include "scene/resources/sky_material.h"
void MaterialEditor::_notification(int p_what) {
@@ -405,17 +405,17 @@ Ref<Resource> ORMMaterial3DConversionPlugin::convert(const Ref<Resource> &p_reso
return smat;
}
-String ParticlesMaterialConversionPlugin::converts_to() const {
+String ParticleProcessMaterialConversionPlugin::converts_to() const {
return "ShaderMaterial";
}
-bool ParticlesMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) const {
- Ref<ParticlesMaterial> mat = p_resource;
+bool ParticleProcessMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) const {
+ Ref<ParticleProcessMaterial> mat = p_resource;
return mat.is_valid();
}
-Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const {
- Ref<ParticlesMaterial> mat = p_resource;
+Ref<Resource> ParticleProcessMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const {
+ Ref<ParticleProcessMaterial> mat = p_resource;
ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>());
Ref<ShaderMaterial> smat;
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index fc3da5fd9f..06ae43e6d7 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -122,8 +122,8 @@ public:
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
};
-class ParticlesMaterialConversionPlugin : public EditorResourceConversionPlugin {
- GDCLASS(ParticlesMaterialConversionPlugin, EditorResourceConversionPlugin);
+class ParticleProcessMaterialConversionPlugin : public EditorResourceConversionPlugin {
+ GDCLASS(ParticleProcessMaterialConversionPlugin, EditorResourceConversionPlugin);
public:
virtual String converts_to() const override;
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 8f1e6c9ec2..043848080f 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -52,10 +52,10 @@
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
+#include "scene/3d/marker_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation_region_3d.h"
#include "scene/3d/occluder_instance_3d.h"
-#include "scene/3d/position_3d.h"
#include "scene/3d/ray_cast_3d.h"
#include "scene/3d/reflection_probe.h"
#include "scene/3d/shape_cast_3d.h"
@@ -2292,7 +2292,7 @@ void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
///
-Position3DGizmoPlugin::Position3DGizmoPlugin() {
+Marker3DGizmoPlugin::Marker3DGizmoPlugin() {
pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
cursor_points = Vector<Vector3>();
@@ -2316,7 +2316,7 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() {
// Use the axis color which is brighter for the positive axis.
// Use a darkened axis color for the negative axis.
- // This makes it possible to see in which direction the Position3D node is rotated
+ // This makes it possible to see in which direction the Marker3D node is rotated
// (which can be important depending on how it's used).
const Color color_x = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor"));
cursor_colors.push_back(color_x);
@@ -2352,19 +2352,19 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() {
pos3d_mesh->surface_set_material(0, mat);
}
-bool Position3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<Position3D>(p_spatial) != nullptr;
+bool Marker3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<Marker3D>(p_spatial) != nullptr;
}
-String Position3DGizmoPlugin::get_gizmo_name() const {
- return "Position3D";
+String Marker3DGizmoPlugin::get_gizmo_name() const {
+ return "Marker3D";
}
-int Position3DGizmoPlugin::get_priority() const {
+int Marker3DGizmoPlugin::get_priority() const {
return -1;
}
-void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+void Marker3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->clear();
p_gizmo->add_mesh(pos3d_mesh);
p_gizmo->add_collision_segments(cursor_points);
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 739bf1b929..7dac1bd360 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -334,8 +334,8 @@ public:
Label3DGizmoPlugin();
};
-class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin);
+class Marker3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(Marker3DGizmoPlugin, EditorNode3DGizmoPlugin);
Ref<ArrayMesh> pos3d_mesh;
Vector<Vector3> cursor_points;
@@ -346,7 +346,7 @@ public:
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
- Position3DGizmoPlugin();
+ Marker3DGizmoPlugin();
};
class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin {
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 2798f3d93e..1214024098 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -46,6 +46,7 @@
#include "editor/scene_tree_dock.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/collision_shape_3d.h"
+#include "scene/3d/decal.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics_body_3d.h"
@@ -2972,6 +2973,13 @@ void Node3DEditorViewport::_menu_option(int p_option) {
xform.scale_basis(sp->get_scale());
}
+ if (Object::cast_to<Decal>(E)) {
+ // Adjust rotation to match Decal's default orientation.
+ // This makes the decal "look" in the same direction as the camera,
+ // rather than pointing down relative to the camera orientation.
+ xform.basis.rotate_local(Vector3(1, 0, 0), Math_TAU * 0.25);
+ }
+
undo_redo->add_do_method(sp, "set_global_transform", xform);
undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
@@ -2999,7 +3007,16 @@ void Node3DEditorViewport::_menu_option(int p_option) {
continue;
}
- undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_euler_normalized());
+ Basis basis = camera_transform.basis;
+
+ if (Object::cast_to<Decal>(E)) {
+ // Adjust rotation to match Decal's default orientation.
+ // This makes the decal "look" in the same direction as the camera,
+ // rather than pointing down relative to the camera orientation.
+ basis.rotate_local(Vector3(1, 0, 0), Math_TAU * 0.25);
+ }
+
+ undo_redo->add_do_method(sp, "set_rotation", basis.get_euler_normalized());
undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
}
undo_redo->commit_action();
@@ -3964,19 +3981,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
String name = path.get_file().get_basename();
- switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) {
- case NAME_CASING_PASCAL_CASE:
- name = name.capitalize().replace(" ", "");
- break;
- case NAME_CASING_CAMEL_CASE:
- name = name.capitalize().replace(" ", "");
- name[0] = name.to_lower()[0];
- break;
- case NAME_CASING_SNAKE_CASE:
- name = name.capitalize().replace(" ", "_").to_lower();
- break;
- }
- mesh_instance->set_name(name);
+ mesh_instance->set_name(Node::adjust_name_casing(name));
instantiated_scene = mesh_instance;
} else {
@@ -4113,6 +4118,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
continue;
}
Ref<PackedScene> scn = res;
+ Ref<Mesh> mesh = res;
Ref<Material> mat = res;
Ref<Texture2D> tex = res;
if (scn.is_valid()) {
@@ -4131,6 +4137,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
spatial_editor->set_preview_material(mat);
break;
+ } else if (mesh.is_valid()) {
+ // Let the mesh pass.
} else if (tex.is_valid()) {
Ref<StandardMaterial3D> new_mat = memnew(StandardMaterial3D);
new_mat->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, tex);
@@ -7495,7 +7503,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin)));
add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin)));
add_gizmo_plugin(Ref<Label3DGizmoPlugin>(memnew(Label3DGizmoPlugin)));
- add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<Marker3DGizmoPlugin>(memnew(Marker3DGizmoPlugin)));
add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin)));
add_gizmo_plugin(Ref<ShapeCast3DGizmoPlugin>(memnew(ShapeCast3DGizmoPlugin)));
add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin)));
@@ -7615,7 +7623,7 @@ void Node3DEditor::_load_default_preview_settings() {
environ_tonemap_button->set_pressed(true);
environ_ao_button->set_pressed(false);
environ_gi_button->set_pressed(false);
- sun_max_distance->set_value(250);
+ sun_max_distance->set_value(100);
sun_color->set_pick_color(Color(1, 1, 1));
sun_energy->set_value(1.0);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index de13c77e1a..a0c9ddb14b 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -49,7 +49,6 @@
#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
-#include "modules/visual_script/editor/visual_script_editor.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@@ -66,12 +65,12 @@ String EditorSyntaxHighlighter::_get_name() const {
return "Unnamed";
}
-Array EditorSyntaxHighlighter::_get_supported_languages() const {
- Array ret;
+PackedStringArray EditorSyntaxHighlighter::_get_supported_languages() const {
+ PackedStringArray ret;
if (GDVIRTUAL_CALL(_get_supported_languages, ret)) {
return ret;
}
- return Array();
+ return PackedStringArray();
}
Ref<EditorSyntaxHighlighter> EditorSyntaxHighlighter::_create() const {
@@ -1156,8 +1155,8 @@ Ref<Script> ScriptEditor::_get_current_script() {
}
}
-Array ScriptEditor::_get_open_scripts() const {
- Array ret;
+TypedArray<Script> ScriptEditor::_get_open_scripts() const {
+ TypedArray<Script> ret;
Vector<Ref<Script>> scripts = get_open_scripts();
int scrits_amount = scripts.size();
for (int idx_script = 0; idx_script < scrits_amount; idx_script++) {
@@ -1732,7 +1731,7 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
continue;
}
- Array bpoints = se->get_breakpoints();
+ PackedInt32Array bpoints = se->get_breakpoints();
for (int j = 0; j < bpoints.size(); j++) {
p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1));
}
@@ -2380,8 +2379,8 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col,
se->add_syntax_highlighter(highlighter);
if (script != nullptr && !highlighter_set) {
- Array languages = highlighter->_get_supported_languages();
- if (languages.find(script->get_language()->get_name()) > -1) {
+ PackedStringArray languages = highlighter->_get_supported_languages();
+ if (languages.has(script->get_language()->get_name())) {
se->set_syntax_highlighter(highlighter);
highlighter_set = true;
}
@@ -3446,8 +3445,8 @@ Vector<Ref<Script>> ScriptEditor::get_open_scripts() const {
return out_scripts;
}
-Array ScriptEditor::_get_open_script_editors() const {
- Array script_editors;
+TypedArray<ScriptEditorBase> ScriptEditor::_get_open_script_editors() const {
+ TypedArray<ScriptEditorBase> script_editors;
for (int i = 0; i < tab_container->get_tab_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
if (!se) {
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 9f088aac49..d1898efb69 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -59,11 +59,11 @@ protected:
static void _bind_methods();
GDVIRTUAL0RC(String, _get_name)
- GDVIRTUAL0RC(Array, _get_supported_languages)
+ GDVIRTUAL0RC(PackedStringArray, _get_supported_languages)
public:
virtual String _get_name() const;
- virtual Array _get_supported_languages() const;
+ virtual PackedStringArray _get_supported_languages() const;
void _set_edited_resource(const Ref<Resource> &p_res) { edited_resourse = p_res; }
Ref<RefCounted> _get_edited_resource() { return edited_resourse; }
@@ -156,7 +156,7 @@ public:
virtual void ensure_focus() = 0;
virtual void tag_saved_version() = 0;
virtual void reload(bool p_soft) {}
- virtual Array get_breakpoints() = 0;
+ virtual PackedInt32Array get_breakpoints() = 0;
virtual void set_breakpoint(int p_line, bool p_enabled) = 0;
virtual void clear_breakpoints() = 0;
virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0;
@@ -386,7 +386,7 @@ class ScriptEditor : public PanelContainer {
Array _get_cached_breakpoints_for_script(const String &p_path) const;
ScriptEditorBase *_get_current_editor() const;
- Array _get_open_script_editors() const;
+ TypedArray<ScriptEditorBase> _get_open_script_editors() const;
Ref<ConfigFile> script_editor_cache;
void _save_editor_state(ScriptEditorBase *p_editor);
@@ -452,7 +452,7 @@ class ScriptEditor : public PanelContainer {
void _file_dialog_action(String p_file);
Ref<Script> _get_current_script();
- Array _get_open_scripts() const;
+ TypedArray<Script> _get_open_scripts() const;
HashSet<String> textfile_extensions;
Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 5d5f452390..5e7db17edf 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -596,7 +596,7 @@ void ScriptTextEditor::_update_bookmark_list() {
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
- Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
+ PackedInt32Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
if (bookmark_list.size() == 0) {
return;
}
@@ -751,7 +751,7 @@ void ScriptTextEditor::_update_breakpoint_list() {
breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_breakpoint"), DEBUG_GOTO_NEXT_BREAKPOINT);
breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_breakpoint"), DEBUG_GOTO_PREV_BREAKPOINT);
- Array breakpoint_list = code_editor->get_text_editor()->get_breakpointed_lines();
+ PackedInt32Array breakpoint_list = code_editor->get_text_editor()->get_breakpointed_lines();
if (breakpoint_list.size() == 0) {
return;
}
@@ -1264,7 +1264,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
} break;
case DEBUG_REMOVE_ALL_BREAKPOINTS: {
- Array bpoints = tx->get_breakpointed_lines();
+ PackedInt32Array bpoints = tx->get_breakpointed_lines();
for (int i = 0; i < bpoints.size(); i++) {
int line = bpoints[i];
@@ -1274,7 +1274,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
} break;
case DEBUG_GOTO_NEXT_BREAKPOINT: {
- Array bpoints = tx->get_breakpointed_lines();
+ PackedInt32Array bpoints = tx->get_breakpointed_lines();
if (bpoints.size() <= 0) {
return;
}
@@ -1300,7 +1300,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case DEBUG_GOTO_PREV_BREAKPOINT: {
- Array bpoints = tx->get_breakpointed_lines();
+ PackedInt32Array bpoints = tx->get_breakpointed_lines();
if (bpoints.size() <= 0) {
return;
}
@@ -1441,7 +1441,7 @@ void ScriptTextEditor::reload(bool p_soft) {
scr->get_language()->reload_tool_script(scr, soft);
}
-Array ScriptTextEditor::get_breakpoints() {
+PackedInt32Array ScriptTextEditor::get_breakpoints() {
return code_editor->get_text_editor()->get_breakpointed_lines();
}
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index fc87c84a2c..8d2fb98721 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -229,7 +229,7 @@ public:
virtual void clear_executing_line() override;
virtual void reload(bool p_soft) override;
- virtual Array get_breakpoints() override;
+ virtual PackedInt32Array get_breakpoints() override;
virtual void set_breakpoint(int p_line, bool p_enabled) override;
virtual void clear_breakpoints() override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index d70c50f72a..53bc6fbdf4 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -994,7 +994,7 @@ void ShaderEditor::_update_bookmark_list() {
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
- Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
+ PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
if (bookmark_list.size() == 0) {
return;
}
@@ -1257,6 +1257,17 @@ void ShaderEditorPlugin::_update_shader_list_status() {
}
}
+void ShaderEditorPlugin::_move_shader_tab(int p_from, int p_to) {
+ if (p_from == p_to) {
+ return;
+ }
+ EditedShader es = edited_shaders[p_from];
+ edited_shaders.remove_at(p_from);
+ edited_shaders.insert(p_to, es);
+ shader_tabs->move_child(shader_tabs->get_tab_control(p_from), p_to);
+ _update_shader_list();
+}
+
void ShaderEditorPlugin::edit(Object *p_object) {
EditedShader es;
@@ -1451,6 +1462,109 @@ void ShaderEditorPlugin::_shader_include_created(Ref<ShaderInclude> p_shader_inc
EditorNode::get_singleton()->push_item(p_shader_inc.ptr());
}
+Variant ShaderEditorPlugin::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
+ if (shader_list->get_item_count() == 0) {
+ return Variant();
+ }
+
+ int idx = shader_list->get_item_at_position(p_point);
+ if (idx < 0) {
+ return Variant();
+ }
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+ String preview_name = shader_list->get_item_text(idx);
+ Ref<Texture2D> preview_icon = shader_list->get_item_icon(idx);
+
+ if (!preview_icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(preview_icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(preview_name));
+ drag_preview->add_child(label);
+ main_split->set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "shader_list_element";
+ drag_data["shader_list_element"] = idx;
+
+ return drag_data;
+}
+
+bool ShaderEditorPlugin::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return false;
+ }
+
+ if (String(d["type"]) == "shader_list_element") {
+ return true;
+ }
+
+ if (String(d["type"]) == "files") {
+ Vector<String> files = d["files"];
+
+ if (files.size() == 0) {
+ return false;
+ }
+
+ for (int i = 0; i < files.size(); i++) {
+ String file = files[i];
+ if (ResourceLoader::exists(file, "Shader")) {
+ Ref<Shader> shader = ResourceLoader::load(file);
+ if (shader.is_valid()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ return false;
+}
+
+void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ if (!can_drop_data_fw(p_point, p_data, p_from)) {
+ return;
+ }
+
+ Dictionary d = p_data;
+ if (!d.has("type")) {
+ return;
+ }
+
+ if (String(d["type"]) == "shader_list_element") {
+ int idx = d["shader_list_element"];
+ int new_idx = shader_list->get_item_at_position(p_point);
+ _move_shader_tab(idx, new_idx);
+ return;
+ }
+
+ if (String(d["type"]) == "files") {
+ Vector<String> files = d["files"];
+
+ for (int i = 0; i < files.size(); i++) {
+ String file = files[i];
+ if (!ResourceLoader::exists(file, "Shader")) {
+ continue;
+ }
+
+ Ref<Resource> res = ResourceLoader::load(file);
+ if (res.is_valid()) {
+ edit(res.ptr());
+ }
+ }
+ }
+}
+
+void ShaderEditorPlugin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_get_drag_data_fw", "point", "from"), &ShaderEditorPlugin::get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::drop_data_fw);
+}
+
ShaderEditorPlugin::ShaderEditorPlugin() {
main_split = memnew(HSplitContainer);
@@ -1483,6 +1597,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
vb->add_child(shader_list);
shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected));
shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked));
+ shader_list->set_drag_forwarding(this);
main_split->add_child(vb);
vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE);
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 0980cc4db2..afd38ef71a 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -250,6 +250,14 @@ class ShaderEditorPlugin : public EditorPlugin {
void _shader_created(Ref<Shader> p_shader);
void _shader_include_created(Ref<ShaderInclude> p_shader_inc);
void _update_shader_list_status();
+ void _move_shader_tab(int p_from, int p_to);
+
+ Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
+ bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+ void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+
+protected:
+ static void _bind_methods();
public:
virtual void edit(Object *p_object) override;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 1e4ef217f0..c25f2bb25c 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -31,7 +31,6 @@
#include "skeleton_3d_editor_plugin.h"
#include "core/io/resource_saver.h"
-#include "editor/editor_file_dialog.h"
#include "editor/editor_node.h"
#include "editor/editor_properties.h"
#include "editor/editor_scale.h"
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index f51d4e60e8..9747ed8374 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -31,6 +31,7 @@
#ifndef SKELETON_3D_EDITOR_PLUGIN_H
#define SKELETON_3D_EDITOR_PLUGIN_H
+#include "editor/editor_file_dialog.h"
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "node_3d_editor_plugin.h"
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 196d87da36..0900415b04 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -128,8 +128,8 @@ Control *TextEditor::get_base_editor() const {
return code_editor->get_text_editor();
}
-Array TextEditor::get_breakpoints() {
- return Array();
+PackedInt32Array TextEditor::get_breakpoints() {
+ return PackedInt32Array();
}
void TextEditor::reload_text() {
@@ -165,7 +165,7 @@ void TextEditor::_update_bookmark_list() {
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
- Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
+ PackedInt32Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
if (bookmark_list.size() == 0) {
return;
}
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 4f0121da52..15f7c45653 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -118,7 +118,7 @@ public:
virtual Variant get_edit_state() override;
virtual void set_edit_state(const Variant &p_state) override;
virtual Vector<String> get_functions() override;
- virtual Array get_breakpoints() override;
+ virtual PackedInt32Array get_breakpoints() override;
virtual void set_breakpoint(int p_line, bool p_enabled) override{};
virtual void clear_breakpoints() override{};
virtual void goto_line(int p_line, bool p_with_error = false) override;
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index f119ada810..54ba28833c 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -127,8 +127,8 @@ void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
}
// Update the backgrounds.
- background_left->update();
- background_right->update();
+ background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
+ background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
// Zoom on the position.
if (p_zoom_on_mouse_pos) {
@@ -374,13 +374,11 @@ void TileAtlasView::_draw_alternatives() {
void TileAtlasView::_draw_background_left() {
Ref<Texture2D> texture = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons"));
- background_left->set_size(base_tiles_root_control->get_custom_minimum_size());
background_left->draw_texture_rect(texture, Rect2(Vector2(), background_left->get_size()), true);
}
void TileAtlasView::_draw_background_right() {
Ref<Texture2D> texture = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons"));
- background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size());
background_right->draw_texture_rect(texture, Rect2(Vector2(), background_right->get_size()), true);
}
@@ -405,23 +403,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
// Update everything.
_update_zoom_and_panning();
- // Change children control size.
- Size2i base_tiles_control_size = _compute_base_tiles_control_size();
- for (int i = 0; i < base_tiles_drawing_root->get_child_count(); i++) {
- Control *control = Object::cast_to<Control>(base_tiles_drawing_root->get_child(i));
- if (control) {
- control->set_size(base_tiles_control_size);
- }
- }
-
- Size2i alternative_control_size = _compute_alternative_tiles_control_size();
- for (int i = 0; i < alternative_tiles_drawing_root->get_child_count(); i++) {
- Control *control = Object::cast_to<Control>(alternative_tiles_drawing_root->get_child(i));
- if (control) {
- control->set_size(alternative_control_size);
- }
- }
-
// Update.
base_tiles_draw->update();
base_tiles_texture_grid->update();
@@ -613,7 +594,7 @@ TileAtlasView::TileAtlasView() {
background_left = memnew(Control);
background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- background_left->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left));
base_tiles_root_control->add_child(background_left);
@@ -651,23 +632,26 @@ TileAtlasView::TileAtlasView() {
alternative_tiles_root_control = memnew(Control);
alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+ alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input));
right_vbox->add_child(alternative_tiles_root_control);
background_right = memnew(Control);
background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT);
background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED);
background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right));
-
alternative_tiles_root_control->add_child(background_right);
alternative_tiles_drawing_root = memnew(Control);
alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ alternative_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST);
alternative_tiles_root_control->add_child(alternative_tiles_drawing_root);
alternatives_draw = memnew(Control);
alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives));
alternative_tiles_drawing_root->add_child(alternatives_draw);
}
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 196a642283..1c0b622bb1 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -136,6 +136,7 @@ public:
} else {
base_tiles_root_control->add_child(p_control);
}
+ p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
@@ -149,6 +150,7 @@ public:
} else {
alternative_tiles_root_control->add_child(p_control);
}
+ p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS);
};
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 09722d3d65..6bdf279537 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1888,7 +1888,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
alternative_tiles_control_unscaled->update();
if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) {
- if (Vector2(drag_start_mouse_pos).distance_to(tile_atlas_control->get_local_mouse_position()) > 5.0 * EDSCALE) {
+ if (Vector2(drag_start_mouse_pos).distance_to(alternative_tiles_control->get_local_mouse_position()) > 5.0 * EDSCALE) {
drag_type = DRAG_TYPE_NONE;
}
}
@@ -1896,8 +1896,6 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- drag_type = DRAG_TYPE_NONE;
-
Vector2 mouse_local_pos = alternative_tiles_control->get_local_mouse_position();
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
@@ -1919,25 +1917,29 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
if (mb->is_pressed()) {
drag_type = DRAG_TYPE_MAY_POPUP_MENU;
drag_start_mouse_pos = alternative_tiles_control->get_local_mouse_position();
- } else if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) {
- // Right click released and wasn't dragged too far
- Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos);
+ } else {
+ if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) {
+ // Right click released and wasn't dragged too far
+ Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos);
- selection.clear();
- TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
- if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
- selection.insert(selected);
- }
+ selection.clear();
+ TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) };
+ if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) {
+ selection.insert(selected);
+ }
- _update_tile_inspector();
- _update_tile_id_label();
+ _update_tile_inspector();
+ _update_tile_id_label();
- if (selection.size() == 1) {
- selected = selection.front()->get();
- menu_option_coords = selected.tile;
- menu_option_alternative = selected.alternative;
- alternative_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
+ if (selection.size() == 1) {
+ selected = selection.front()->get();
+ menu_option_coords = selected.tile;
+ menu_option_alternative = selected.alternative;
+ alternative_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i()));
+ }
}
+
+ drag_type = DRAG_TYPE_NONE;
}
}
tile_atlas_control->update();
@@ -2524,7 +2526,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control);
tile_atlas_control_unscaled = memnew(Control);
- tile_atlas_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
tile_atlas_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw));
tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control_unscaled, false);
tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
@@ -2541,7 +2542,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
alternative_tiles_control_unscaled = memnew(Control);
- alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw));
tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false);
alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index cf55465417..6db499f2c7 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -242,7 +242,7 @@ void VersionControlEditorPlugin::_view_file_diff() {
}
void VersionControlEditorPlugin::_display_file_diff(String p_file_path) {
- Array diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path);
+ TypedArray<Dictionary> diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path);
diff_file_name->set_text(p_file_path);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 961f092650..e048ee2698 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -4312,6 +4312,9 @@ void VisualShaderEditor::_update_varying_tree() {
case VisualShader::VARYING_TYPE_FLOAT:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")));
break;
+ case VisualShader::VARYING_TYPE_INT:
+ item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")));
+ break;
case VisualShader::VARYING_TYPE_VECTOR_2D:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")));
break;
@@ -4321,8 +4324,8 @@ void VisualShaderEditor::_update_varying_tree() {
case VisualShader::VARYING_TYPE_VECTOR_4D:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")));
break;
- case VisualShader::VARYING_TYPE_COLOR:
- item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")));
+ case VisualShader::VARYING_TYPE_BOOLEAN:
+ item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")));
break;
case VisualShader::VARYING_TYPE_TRANSFORM:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")));
@@ -4963,10 +4966,11 @@ VisualShaderEditor::VisualShaderEditor() {
varying_type = memnew(OptionButton);
hb->add_child(varying_type);
varying_type->add_item("Float");
+ varying_type->add_item("Int");
varying_type->add_item("Vector2");
varying_type->add_item("Vector3");
varying_type->add_item("Vector4");
- varying_type->add_item("Color");
+ varying_type->add_item("Boolean");
varying_type->add_item("Transform");
varying_name = memnew(LineEdit);
@@ -5759,10 +5763,11 @@ public:
Ref<Texture2D> type_icon[] = {
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
+ EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")),
- EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
+ EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
};