summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/Camera3D.xml6
-rw-r--r--editor/connections_dialog.cpp2
-rw-r--r--editor/editor_inspector.cpp3
-rw-r--r--editor/editor_plugin_settings.cpp11
-rw-r--r--editor/gui/scene_tree_editor.cpp26
-rw-r--r--editor/node_dock.cpp7
-rw-r--r--editor/node_dock.h2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp17
-rw-r--r--editor/project_manager.cpp1
-rw-r--r--platform/windows/display_server_windows.cpp2
-rw-r--r--scene/3d/camera_3d.cpp68
-rw-r--r--scene/3d/camera_3d.h3
-rw-r--r--scene/animation/animation_node_state_machine.cpp10
-rw-r--r--scene/main/viewport.cpp6
14 files changed, 104 insertions, 60 deletions
diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml
index 9d43b4bd26..83f1ffa08e 100644
--- a/doc/classes/Camera3D.xml
+++ b/doc/classes/Camera3D.xml
@@ -17,6 +17,12 @@
If this is the current camera, remove it from being current. If [param enable_next] is [code]true[/code], request to make the next camera current, if any.
</description>
</method>
+ <method name="get_camera_projection" qualifiers="const">
+ <return type="Projection" />
+ <description>
+ Returns the projection matrix that this camera uses to render to its associated viewport. The camera must be part of the scene tree to function.
+ </description>
+ </method>
<method name="get_camera_rid" qualifiers="const">
<return type="RID" />
<description>
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index f4b7b14e99..3965dcd198 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/scene_tree_editor.h"
+#include "editor/node_dock.h"
#include "editor/scene_tree_dock.h"
#include "plugins/script_editor_plugin.h"
#include "scene/gui/button.h"
@@ -1440,6 +1441,7 @@ ConnectionsDock::ConnectionsDock() {
connect_button->connect("pressed", callable_mp(this, &ConnectionsDock::_connect_pressed));
connect_dialog = memnew(ConnectDialog);
+ connect_dialog->connect("connected", callable_mp(NodeDock::get_singleton(), &NodeDock::restore_last_valid_node), CONNECT_DEFERRED);
add_child(connect_dialog);
disconnect_all_dialog = memnew(ConfirmationDialog);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index e3514b4691..367373dc17 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3184,10 +3184,11 @@ void EditorInspector::update_tree() {
if (val.enumeration == enum_name && !val.name.ends_with("_MAX")) {
const String enum_value = EditorPropertyNameProcessor::get_singleton()->process_name(val.name, EditorPropertyNameProcessor::STYLE_CAPITALIZED);
// Prettify the enum value display, so that "<ENUM NAME>_<VALUE>" becomes "Value".
+ String desc = DTR(val.description).trim_prefix("\n");
doc_info.description += vformat(
"\n[b]%s:[/b] %s",
enum_value.trim_prefix(EditorPropertyNameProcessor::get_singleton()->process_name(enum_name, EditorPropertyNameProcessor::STYLE_CAPITALIZED) + " "),
- DTR(val.description).trim_prefix("\n"));
+ desc.is_empty() ? ("[i]" + TTR("No description.") + "[/i]") : desc);
}
}
}
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index 1e582992d1..7f57619ac8 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -101,9 +101,18 @@ void EditorPluginSettings::update_plugins() {
String description = cf->get_value("plugin", "description");
String scr = cf->get_value("plugin", "script");
+ const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);
+ String wrapped_description;
+
+ for (int j = 0; j < boundaries.size(); j += 2) {
+ const int start = boundaries[j];
+ const int end = boundaries[j + 1];
+ wrapped_description += "\n" + description.substr(start, end - start + 1).rstrip("\n");
+ }
+
TreeItem *item = plugin_list->create_item(root);
item->set_text(0, name);
- item->set_tooltip_text(0, TTR("Name:") + " " + name + "\n" + TTR("Path:") + " " + path + "\n" + TTR("Main Script:") + " " + scr + "\n" + TTR("Description:") + " " + description);
+ item->set_tooltip_text(0, TTR("Name:") + " " + name + "\n" + TTR("Path:") + " " + path + "\n" + TTR("Main Script:") + " " + scr + "\n" + TTR("Description:") + " " + wrapped_description);
item->set_metadata(0, path);
item->set_text(1, version);
item->set_metadata(1, scr);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index d1756df66c..d8c6ff54cc 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -374,7 +374,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
tooltip += String("\n" + TTR("Type:") + " " + (custom_type != StringName() ? String(custom_type) : p_node->get_class()));
if (!p_node->get_editor_description().is_empty()) {
- tooltip += "\n\n" + p_node->get_editor_description();
+ const PackedInt32Array boundaries = TS->string_get_word_breaks(p_node->get_editor_description(), "", 80);
+ tooltip += "\n";
+
+ for (int i = 0; i < boundaries.size(); i += 2) {
+ const int start = boundaries[i];
+ const int end = boundaries[i + 1];
+ tooltip += "\n" + p_node->get_editor_description().substr(start, end - start + 1).rstrip("\n");
+ }
}
item->set_tooltip_text(0, tooltip);
@@ -1019,11 +1026,18 @@ void SceneTreeEditor::_renamed() {
}
}
- if (n->is_unique_name_in_owner() && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name) != nullptr) {
- error->set_text(TTR("Another node already uses this unique name in the scene."));
- error->popup_centered();
- which->set_text(0, n->get_name());
- return;
+ if (n->is_unique_name_in_owner()) {
+ Node *existing = get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name);
+ if (existing == n) {
+ which->set_text(0, n->get_name());
+ return;
+ }
+ if (existing != nullptr) {
+ error->set_text(TTR("Another node already uses this unique name in the scene."));
+ error->popup_centered();
+ which->set_text(0, n->get_name());
+ return;
+ }
}
_rename_node(n, new_name);
diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp
index 545769d327..ebb35eedf9 100644
--- a/editor/node_dock.cpp
+++ b/editor/node_dock.cpp
@@ -70,6 +70,9 @@ void NodeDock::update_lists() {
void NodeDock::set_node(Node *p_node) {
connections->set_node(p_node);
groups->set_current(p_node);
+ if (p_node) {
+ last_valid_node = p_node;
+ }
if (p_node) {
if (connections_button->is_pressed()) {
@@ -88,6 +91,10 @@ void NodeDock::set_node(Node *p_node) {
}
}
+void NodeDock::restore_last_valid_node() {
+ set_node(last_valid_node);
+}
+
NodeDock::NodeDock() {
singleton = this;
diff --git a/editor/node_dock.h b/editor/node_dock.h
index e9dcc41d48..cc22171453 100644
--- a/editor/node_dock.h
+++ b/editor/node_dock.h
@@ -47,6 +47,7 @@ class NodeDock : public VBoxContainer {
HBoxContainer *mode_hb = nullptr;
Label *select_a_node = nullptr;
+ Node *last_valid_node = nullptr;
private:
static NodeDock *singleton;
@@ -60,6 +61,7 @@ protected:
public:
void set_node(Node *p_node);
+ void restore_last_valid_node();
void show_groups();
void show_connections();
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index ef805e6524..7767831ea3 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1185,19 +1185,18 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
String text;
+ // Round floating point precision to 2 digits, as tiles don't have that much space.
switch (value.get_type()) {
- case Variant::INT:
- text = vformat("%d", value);
- break;
case Variant::FLOAT:
text = vformat("%.2f", value);
break;
- case Variant::STRING:
- case Variant::STRING_NAME:
- text = value;
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::VECTOR4:
+ text = vformat("%.2v", value);
break;
default:
- return;
+ text = value.stringify();
break;
}
@@ -1216,8 +1215,8 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
}
Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size);
- p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1));
- p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color);
+ p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 3, Color(0, 0, 0));
+ p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color);
}
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index a86963c330..71b5ef2460 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -2504,6 +2504,7 @@ void ProjectManager::_apply_project_tags() {
callable_mp((Window *)tag_manage_dialog, &Window::show).call_deferred(); // Make sure the dialog does not disappear.
return;
} else {
+ tags.sort();
cfg.set_value("application", "config/tags", tags);
err = cfg.save(project_godot);
if (err != OK) {
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 91f7de7f26..b0b1e1680f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1598,7 +1598,7 @@ void DisplayServerWindows::window_request_attention(WindowID p_window) {
FLASHWINFO info;
info.cbSize = sizeof(FLASHWINFO);
info.hwnd = wd.hWnd;
- info.dwFlags = FLASHW_TRAY;
+ info.dwFlags = FLASHW_ALL;
info.dwTimeout = 0;
info.uCount = 2;
FlashWindowEx(&info);
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 5a4761892d..225b9b35b3 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -170,6 +170,30 @@ Transform3D Camera3D::get_camera_transform() const {
return tr;
}
+Projection Camera3D::_get_camera_projection(real_t p_near) const {
+ Size2 viewport_size = get_viewport()->get_visible_rect().size;
+ Projection cm;
+
+ switch (mode) {
+ case PROJECTION_PERSPECTIVE: {
+ cm.set_perspective(fov, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH);
+ } break;
+ case PROJECTION_ORTHOGONAL: {
+ cm.set_orthogonal(size, viewport_size.aspect(), p_near, far, keep_aspect == KEEP_WIDTH);
+ } break;
+ case PROJECTION_FRUSTUM: {
+ cm.set_frustum(size, viewport_size.aspect(), frustum_offset, p_near, far);
+ } break;
+ }
+
+ return cm;
+}
+
+Projection Camera3D::get_camera_projection() const {
+ ERR_FAIL_COND_V_MSG(!is_inside_tree(), Projection(), "Camera is not inside the scene tree.");
+ return _get_camera_projection(near);
+}
+
void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) {
return;
@@ -286,8 +310,7 @@ Vector3 Camera3D::project_local_ray_normal(const Point2 &p_pos) const {
if (mode == PROJECTION_ORTHOGONAL) {
ray = Vector3(0, 0, -1);
} else {
- Projection cm;
- cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
+ Projection cm = _get_camera_projection(near);
Vector2 screen_he = cm.get_viewport_half_extents();
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -near).normalized();
}
@@ -302,9 +325,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
ERR_FAIL_COND_V(viewport_size.y == 0, Vector3());
- if (mode == PROJECTION_PERSPECTIVE) {
- return get_camera_transform().origin;
- } else {
+ if (mode == PROJECTION_ORTHOGONAL) {
Vector2 pos = cpos / viewport_size;
real_t vsize, hsize;
if (keep_aspect == KEEP_WIDTH) {
@@ -321,6 +342,8 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const {
ray.z = -near;
ray = get_camera_transform().xform(ray);
return ray;
+ } else {
+ return get_camera_transform().origin;
};
};
@@ -333,15 +356,7 @@ bool Camera3D::is_position_behind(const Vector3 &p_pos) const {
Vector<Vector3> Camera3D::get_near_plane_points() const {
ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector<Vector3>(), "Camera is not inside scene.");
- Size2 viewport_size = get_viewport()->get_visible_rect().size;
-
- Projection cm;
-
- if (mode == PROJECTION_ORTHOGONAL) {
- cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- } else {
- cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- }
+ Projection cm = _get_camera_projection(near);
Vector3 endpoints[8];
cm.get_endpoints(Transform3D(), endpoints);
@@ -361,13 +376,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const {
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- Projection cm;
-
- if (mode == PROJECTION_ORTHOGONAL) {
- cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- } else {
- cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- }
+ Projection cm = _get_camera_projection(near);
Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
@@ -389,13 +398,7 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons
}
Size2 viewport_size = get_viewport()->get_visible_rect().size;
- Projection cm;
-
- if (mode == PROJECTION_ORTHOGONAL) {
- cm.set_orthogonal(size, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH);
- } else {
- cm.set_perspective(fov, viewport_size.aspect(), p_z_depth, far, keep_aspect == KEEP_WIDTH);
- }
+ Projection cm = _get_camera_projection(p_z_depth);
Vector2 vp_he = cm.get_viewport_half_extents();
@@ -508,6 +511,7 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform);
+ ClassDB::bind_method(D_METHOD("get_camera_projection"), &Camera3D::get_camera_projection);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov);
ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset);
ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size);
@@ -653,13 +657,7 @@ bool Camera3D::get_cull_mask_value(int p_layer_number) const {
Vector<Plane> Camera3D::get_frustum() const {
ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
- Size2 viewport_size = get_viewport()->get_visible_rect().size;
- Projection cm;
- if (mode == PROJECTION_PERSPECTIVE) {
- cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- } else {
- cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
- }
+ Projection cm = _get_camera_projection(near);
return cm.get_projection_planes(get_camera_transform());
}
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index 420d0cb939..aa302ded4a 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -105,6 +105,8 @@ protected:
static void _bind_methods();
+ Projection _get_camera_projection(real_t p_near) const;
+
public:
enum {
NOTIFICATION_BECAME_CURRENT = 50,
@@ -138,6 +140,7 @@ public:
void set_frustum_offset(Vector2 p_offset);
virtual Transform3D get_camera_transform() const;
+ virtual Projection get_camera_projection() const;
virtual Vector3 project_ray_normal(const Point2 &p_pos) const;
virtual Vector3 project_ray_origin(const Point2 &p_pos) const;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 071e2e7f37..0254419d22 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -901,18 +901,20 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
bool is_state_changed = false;
NextInfo next;
- StringName transition_start = current;
+ Vector<StringName> transition_path;
+ transition_path.push_back(current);
while (true) {
next = _find_next(p_tree, p_state_machine);
- if (next.node == transition_start) {
- is_state_changed = false;
- break; // Maybe infinity loop, do noting more.
+ if (transition_path.has(next.node)) {
+ WARN_PRINT_ONCE_ED("AnimationNodeStateMachinePlayback: " + base_path + "playback aborts the transition by detecting one or more looped transitions in the same frame to prevent to infinity loop. You may need to check the transition settings.");
+ break; // Maybe infinity loop, do nothing more.
}
if (!_can_transition_to_next(p_tree, p_state_machine, next, p_test_only)) {
break; // Finish transition.
}
+ transition_path.push_back(next.node);
is_state_changed = true;
// Setting for fading.
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index faa609d847..8609b77944 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1875,13 +1875,13 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (over != gui.mouse_over) {
+ if (!gui.mouse_over) {
+ _drop_physics_mouseover();
+ }
_drop_mouse_over();
_gui_cancel_tooltip();
if (over) {
- if (!gui.mouse_over) {
- _drop_physics_mouseover();
- }
_gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
gui.mouse_over = over;
}