summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/physics_body_2d.cpp22
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/tile_map.cpp14
-rw-r--r--scene/3d/bone_attachment_3d.cpp2
-rw-r--r--scene/3d/physics_body_3d.cpp14
-rw-r--r--scene/gui/line_edit.cpp13
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/tabs.cpp28
-rw-r--r--scene/gui/text_edit.cpp4
-rw-r--r--scene/gui/tree.cpp122
-rw-r--r--scene/gui/tree.h6
-rw-r--r--scene/resources/primitive_meshes.cpp8
-rw-r--r--scene/resources/primitive_meshes.h2
-rw-r--r--scene/resources/skeleton_modification_2d.cpp42
-rw-r--r--scene/resources/skeleton_modification_3d.cpp40
-rw-r--r--scene/resources/surface_tool.cpp1
16 files changed, 195 insertions, 129 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index c3dc9ab92b..c5d444090b 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1090,9 +1090,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
- if (result.travel.length() > margin) {
- gt.elements[2] -= result.travel.slide(up_direction);
- } else {
+ if (result.travel.length() <= margin + CMP_EPSILON) {
gt.elements[2] -= result.travel;
}
set_global_transform(gt);
@@ -1111,7 +1109,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Avoid to move forward on a wall if floor_block_on_wall is true.
if (p_was_on_floor && !on_floor && !vel_dir_facing_up) {
// If the movement is large the body can be prevented from reaching the walls.
- if (result.travel.length() <= margin) {
+ if (result.travel.length() <= margin + CMP_EPSILON) {
// Cancels the motion.
Transform2D gt = get_global_transform();
gt.elements[2] -= result.travel;
@@ -1240,13 +1238,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
}
void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) {
+ if (on_floor || !was_on_floor || vel_dir_facing_up) {
return;
}
+ // Snap by at least collision margin to keep floor state consistent.
+ real_t length = MAX(floor_snap_length, margin);
+
Transform2D gt = get_global_transform();
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
+ if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
bool apply = true;
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
@@ -1274,12 +1275,15 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
+ if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
return false;
}
+ // Snap by at least collision margin to keep floor state consistent.
+ real_t length = MAX(floor_snap_length, margin);
+
PhysicsServer2D::MotionResult result;
- if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) {
+ if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
@@ -1568,7 +1572,7 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
ADD_GROUP("Moving platform", "moving_platform");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 5d0d98a2df..3836fc4b62 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -334,7 +334,7 @@ private:
int max_slides = 4;
int platform_layer;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
- real_t floor_snap_length = 0;
+ real_t floor_snap_length = 1;
real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t moving_platform_floor_layers = UINT32_MAX;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 03db9c0d32..929233e4e0 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -2034,10 +2034,22 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
return false;
} else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
int index = components[0].trim_prefix("layer_").to_int();
- if (index < 0 || index >= (int)layers.size()) {
+ if (index < 0) {
return false;
}
+ if (index >= (int)layers.size()) {
+ _clear_internals();
+ while (index >= (int)layers.size()) {
+ layers.push_back(TileMapLayer());
+ }
+ _recreate_internals();
+
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
+ update_configuration_warnings();
+ }
+
if (components[1] == "name") {
set_layer_name(index, p_value);
return true;
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index c34c150145..afd11482e3 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -110,7 +110,7 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const {
} else {
Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
if (!parent) {
- warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
+ warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
}
}
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 48da186860..6f0f0bd109 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -1146,8 +1146,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
- real_t travel_total = result.travel.length();
- if (travel_total <= margin + CMP_EPSILON) {
+ if (result.travel.length() <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
}
set_global_transform(gt);
@@ -1186,7 +1185,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Transform3D gt = get_global_transform();
real_t travel_total = result.travel.length();
real_t cancel_dist_max = MIN(0.1, margin * 20);
- if (travel_total < margin + CMP_EPSILON) {
+ if (travel_total <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
} else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls.
gt.origin -= result.travel.slide(up_direction);
@@ -1377,7 +1376,9 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
return;
}
+ // Snap by at least collision margin to keep floor state consistent.
real_t length = MAX(floor_snap_length, margin);
+
Transform3D gt = get_global_transform();
PhysicsServer3D::MotionResult result;
if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
@@ -1403,12 +1404,15 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_zero_approx(floor_snap_length) || up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) {
+ if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) {
return false;
}
+ // Snap by at least collision margin to keep floor state consistent.
+ real_t length = MAX(floor_snap_length, margin);
+
PhysicsServer3D::MotionResult result;
- if (move_and_collide(-up_direction * floor_snap_length, result, margin, true, 4, false, true)) {
+ if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
CollisionState result_state;
// Don't apply direction for any type.
_set_collision_direction(result, result_state, CollisionState());
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index d9acbeb828..09128770d5 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -260,24 +260,29 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
} else {
if (selecting_enabled) {
- if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) {
+ const int triple_click_timeout = 600;
+ const int triple_click_tolerance = 5;
+ const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance;
+
+ if (is_triple_click && text.length()) {
// Triple-click select all.
selection.enabled = true;
selection.begin = 0;
selection.end = text.length();
selection.double_click = true;
- selection.last_dblclk = 0;
+ last_dblclk = 0;
caret_column = selection.begin;
} else if (b->is_double_click()) {
// Double-click select word.
+ last_dblclk = OS::get_singleton()->get_ticks_msec();
+ last_dblclk_pos = b->get_position();
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
for (int i = 0; i < words.size(); i++) {
- if (words[i].x < caret_column && words[i].y > caret_column) {
+ if ((words[i].x < caret_column && words[i].y > caret_column) || (i == words.size() - 1 && caret_column == words[i].y)) {
selection.enabled = true;
selection.begin = words[i].x;
selection.end = words[i].y;
selection.double_click = true;
- selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
caret_column = selection.end;
break;
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e364a79c83..9f28c84f50 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -136,7 +136,6 @@ private:
bool creating = false;
bool double_click = false;
bool drag_attempt = false;
- uint64_t last_dblclk = 0;
} selection;
struct TextOperation {
@@ -153,6 +152,9 @@ private:
bool pressing_inside = false;
} clear_button_status;
+ uint64_t last_dblclk = 0;
+ Vector2 last_dblclk_pos;
+
bool caret_blink_enabled = false;
bool caret_force_displayed = false;
bool draw_caret = true;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 7faa9aca2c..ef34bec347 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -98,29 +98,45 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid()) {
Point2 pos = mm->get_position();
- highlight_arrow = -1;
if (buttons_visible) {
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
if (is_layout_rtl()) {
if (pos.x < decr->get_width()) {
- highlight_arrow = 1;
+ if (highlight_arrow != 1) {
+ highlight_arrow = 1;
+ update();
+ }
} else if (pos.x < incr->get_width() + decr->get_width()) {
- highlight_arrow = 0;
+ if (highlight_arrow != 0) {
+ highlight_arrow = 0;
+ update();
+ }
+ } else if (highlight_arrow != -1) {
+ highlight_arrow = -1;
+ update();
}
} else {
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
if (pos.x > limit_minus_buttons + decr->get_width()) {
- highlight_arrow = 1;
+ if (highlight_arrow != 1) {
+ highlight_arrow = 1;
+ update();
+ }
} else if (pos.x > limit_minus_buttons) {
- highlight_arrow = 0;
+ if (highlight_arrow != 0) {
+ highlight_arrow = 0;
+ update();
+ }
+ } else if (highlight_arrow != -1) {
+ highlight_arrow = -1;
+ update();
}
}
}
_update_hover();
- update();
return;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 09899413f2..5b13e1da0b 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3689,7 +3689,7 @@ void TextEdit::select_word_under_caret() {
int end = 0;
const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
for (int i = 0; i < words.size(); i++) {
- if (words[i].x <= caret.column && words[i].y >= caret.column) {
+ if ((words[i].x < caret.column && words[i].y > caret.column) || (i == words.size() - 1 && caret.column == words[i].y)) {
begin = words[i].x;
end = words[i].y;
break;
@@ -5411,7 +5411,7 @@ void TextEdit::_update_selection_mode_word() {
int end = beg;
Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
for (int i = 0; i < words.size(); i++) {
- if (words[i].x < caret_pos && words[i].y > caret_pos) {
+ if ((words[i].x < caret_pos && words[i].y > caret_pos) || (i == words.size() - 1 && caret_pos == words[i].y)) {
beg = words[i].x;
end = words[i].y;
break;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2be59ca04d..7d7596635c 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -144,6 +144,7 @@ void TreeItem::_change_tree(Tree *p_tree) {
/* cell mode */
void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) {
ERR_FAIL_INDEX(p_column, cells.size());
+
Cell &c = cells.write[p_column];
c.mode = p_mode;
c.min = 0;
@@ -155,8 +156,9 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) {
c.text = "";
c.dirty = true;
c.icon_max_w = 0;
+ c.cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
@@ -167,22 +169,27 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
/* check mode */
void TreeItem::set_checked(int p_column, bool p_checked) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].checked = p_checked;
cells.write[p_column].indeterminate = false;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) {
ERR_FAIL_INDEX(p_column, cells.size());
+
// Prevent uncheck if indeterminate set to false twice
if (p_indeterminate == cells[p_column].indeterminate) {
return;
}
+
cells.write[p_column].indeterminate = p_indeterminate;
cells.write[p_column].checked = false;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_checked(int p_column) const {
@@ -214,8 +221,10 @@ void TreeItem::set_text(int p_column, String p_text) {
}
cells.write[p_column].step = 0;
}
+
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
String TreeItem::get_text(int p_column) const {
@@ -231,7 +240,7 @@ void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_di
cells.write[p_column].dirty = true;
_changed_notify(p_column);
}
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
Control::TextDirection TreeItem::get_text_direction(int p_column) const {
@@ -241,10 +250,12 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const {
void TreeItem::clear_opentype_features(int p_column) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].opentype_features.clear();
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) {
@@ -253,8 +264,9 @@ void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_va
if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) {
cells.write[p_column].opentype_features[tag] = p_value;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -269,11 +281,13 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const {
void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) {
ERR_FAIL_INDEX(p_column, cells.size());
+
if (cells[p_column].st_parser != p_parser) {
cells.write[p_column].st_parser = p_parser;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -284,10 +298,12 @@ Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_
void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].st_args = p_args;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Array TreeItem::get_structured_text_bidi_override_options(int p_column) const {
@@ -297,11 +313,13 @@ Array TreeItem::get_structured_text_bidi_override_options(int p_column) const {
void TreeItem::set_language(int p_column, const String &p_language) {
ERR_FAIL_INDEX(p_column, cells.size());
+
if (cells[p_column].language != p_language) {
cells.write[p_column].language = p_language;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -312,10 +330,11 @@ String TreeItem::get_language(int p_column) const {
void TreeItem::set_suffix(int p_column, String p_suffix) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].suffix = p_suffix;
+ cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
String TreeItem::get_suffix(int p_column) const {
@@ -325,9 +344,11 @@ String TreeItem::get_suffix(int p_column) const {
void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon = p_icon;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Ref<Texture2D> TreeItem::get_icon(int p_column) const {
@@ -337,9 +358,11 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const {
void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon_region = p_icon_region;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Rect2 TreeItem::get_icon_region(int p_column) const {
@@ -360,9 +383,11 @@ Color TreeItem::get_icon_modulate(int p_column) const {
void TreeItem::set_icon_max_width(int p_column, int p_max) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon_max_w = p_max;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
int TreeItem::get_icon_max_width(int p_column) const {
@@ -474,8 +499,11 @@ void TreeItem::uncollapse_tree() {
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
+
+ for (Cell &c : cells)
+ c.cached_minimum_size_dirty = true;
+
_changed_notify();
- cached_minimum_size_dirty = true;
}
int TreeItem::get_custom_minimum_height() const {
@@ -799,8 +827,9 @@ void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id
button.disabled = p_disabled;
button.tooltip = p_tooltip;
cells.write[p_column].buttons.push_back(button);
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
int TreeItem::get_button_count(int p_column) const {
@@ -843,8 +872,9 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto
ERR_FAIL_INDEX(p_column, cells.size());
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
cells.write[p_column].buttons.write[p_idx].texture = p_button;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) {
@@ -859,8 +889,9 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) {
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
cells.write[p_column].buttons.write[p_idx].disabled = p_disabled;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_button_disabled(int p_column, int p_idx) const {
@@ -872,9 +903,11 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const {
void TreeItem::set_editable(int p_column, bool p_editable) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].editable = p_editable;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_editable(int p_column) {
@@ -906,8 +939,9 @@ void TreeItem::clear_custom_color(int p_column) {
void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_font = p_font;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
Ref<Font> TreeItem::get_custom_font(int p_column) const {
@@ -917,8 +951,9 @@ Ref<Font> TreeItem::get_custom_font(int p_column) const {
void TreeItem::set_custom_font_size(int p_column, int p_font_size) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_font_size = p_font_size;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
int TreeItem::get_custom_font_size(int p_column) const {
@@ -961,8 +996,9 @@ Color TreeItem::get_custom_bg_color(int p_column) const {
void TreeItem::set_custom_as_button(int p_column, bool p_button) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_button = p_button;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
bool TreeItem::is_custom_set_as_button(int p_column) const {
@@ -972,9 +1008,11 @@ bool TreeItem::is_custom_set_as_button(int p_column) const {
void TreeItem::set_text_align(int p_column, TextAlign p_align) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].text_align = p_align;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
@@ -984,9 +1022,11 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
void TreeItem::set_expand_right(int p_column, bool p_enable) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].expand_right = p_enable;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::get_expand_right(int p_column) const {
@@ -996,8 +1036,11 @@ bool TreeItem::get_expand_right(int p_column) const {
void TreeItem::set_disable_folding(bool p_disable) {
disable_folding = p_disable;
+
+ for (Cell &c : cells)
+ c.cached_minimum_size_dirty = true;
+
_changed_notify(0);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_folding_disabled() const {
@@ -1009,14 +1052,12 @@ Size2 TreeItem::get_minimum_size(int p_column) {
Tree *tree = get_tree();
ERR_FAIL_COND_V(!tree, Size2());
- if (cached_minimum_size_dirty) {
- Size2 size;
+ const TreeItem::Cell &cell = cells[p_column];
- // Default offset?
- //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin;
+ if (cell.cached_minimum_size_dirty) {
+ Size2 size;
// Text.
- const TreeItem::Cell &cell = cells[p_column];
if (!cell.text.is_empty()) {
if (cell.dirty) {
tree->update_item_cell(this, p_column);
@@ -1052,11 +1093,11 @@ Size2 TreeItem::get_minimum_size(int p_column) {
size.width += (cell.buttons.size() - 1) * tree->cache.button_margin;
}
- cached_minimum_size = size;
- cached_minimum_size_dirty = false;
+ cells.write[p_column].cached_minimum_size = size;
+ cells.write[p_column].cached_minimum_size_dirty = false;
}
- return cached_minimum_size;
+ return cell.cached_minimum_size;
}
Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@@ -1337,10 +1378,6 @@ void Tree::update_cache() {
cache.title_button_color = get_theme_color(SNAME("title_button_color"));
v_scroll->set_custom_step(cache.font->get_height(cache.font_size));
-
- for (TreeItem *item = get_root(); item; item = item->get_next()) {
- item->cached_minimum_size_dirty = true;
- }
}
int Tree::compute_item_height(TreeItem *p_item) const {
@@ -4001,10 +4038,12 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) {
int Tree::get_column_minimum_width(int p_column) const {
ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
+ // Use the custom minimum width.
int min_width = columns[p_column].custom_min_width;
+ // Check if the visible title of the column is wider.
if (show_column_titles) {
- min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width);
+ min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
}
if (!columns[p_column].clip_content) {
@@ -4029,7 +4068,11 @@ int Tree::get_column_minimum_width(int p_column) const {
Size2 item_size = item->get_minimum_size(p_column);
if (p_column == 0) {
item_size.width += cache.item_margin * depth;
+ } else {
+ item_size.width += cache.hseparation;
}
+
+ // Check if the item is wider.
min_width = MAX(min_width, item_size.width);
}
}
@@ -4069,9 +4112,6 @@ int Tree::get_column_width(int p_column) const {
}
}
- if (p_column < columns.size() - 1) {
- column_width += cache.hseparation;
- }
return column_width;
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 85fed941dc..c4a6b6b058 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -95,6 +95,9 @@ private:
bool expand_right = false;
Color icon_color = Color(1, 1, 1);
+ Size2i cached_minimum_size;
+ bool cached_minimum_size_dirty = true;
+
TextAlign text_align = ALIGN_LEFT;
Variant meta;
@@ -130,9 +133,6 @@ private:
bool disable_folding = false;
int custom_min_height = 0;
- Size2i cached_minimum_size;
- bool cached_minimum_size_dirty = true;
-
TreeItem *parent = nullptr; // parent item
TreeItem *prev = nullptr; // previous in list
TreeItem *next = nullptr; // next in list
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index e7da41db9d..f8be00f5fb 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset");
}
+uint32_t QuadMesh::surface_get_format(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, 1, 0);
+
+ return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV;
+}
+
QuadMesh::QuadMesh() {
primitive_type = PRIMITIVE_TRIANGLES;
}
@@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
} else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p);
- Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
+ Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
normals.push_back(normal.normalized());
};
ADD_TANGENT(z, 0.0, -x, 1.0)
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 7915cb0028..d447dad97a 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -285,6 +285,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ virtual uint32_t surface_get_format(int p_idx) const override;
+
QuadMesh();
void set_size(const Size2 &p_size);
diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index e533fb054a..7ac40b497d 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- float tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
@@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
arc_angle_max = (Math_PI * 2) + arc_angle_max;
}
if (arc_angle_min > arc_angle_max) {
- float tmp = arc_angle_min;
- arc_angle_min = arc_angle_max;
- arc_angle_max = tmp;
+ SWAP(arc_angle_min, arc_angle_max);
}
arc_angle_min += p_operation_bone->get_bone_angle();
arc_angle_max += p_operation_bone->get_bone_angle();
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
index ee02ede2d5..b476952d86 100644
--- a/scene/resources/skeleton_modification_3d.cpp
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- real_t tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index d5e370568d..a8cd872408 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -1176,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun
ERR_FAIL_COND_V(simplify_func == nullptr, lod);
ERR_FAIL_COND_V(vertex_array.size() == 0, lod);
ERR_FAIL_COND_V(index_array.size() == 0, lod);
+ ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod);
lod.resize(index_array.size());
LocalVector<float> vertices; //uses floats