summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/polygon_2d.cpp30
-rw-r--r--scene/2d/tile_map.cpp126
-rw-r--r--scene/2d/tile_map.h5
-rw-r--r--scene/animation/animation_mixer.cpp7
-rw-r--r--scene/animation/animation_node_state_machine.cpp4
-rw-r--r--scene/animation/animation_tree.cpp18
-rw-r--r--scene/animation/animation_tree.h2
-rw-r--r--scene/gui/color_picker.cpp2
-rw-r--r--scene/gui/label.cpp28
-rw-r--r--scene/gui/label.h4
-rw-r--r--scene/gui/line_edit.cpp21
-rw-r--r--scene/gui/rich_text_label.cpp6
-rw-r--r--scene/gui/tab_bar.cpp4
-rw-r--r--scene/gui/text_edit.cpp3
-rw-r--r--scene/gui/tree.cpp16
-rw-r--r--scene/main/resource_preloader.cpp5
-rw-r--r--scene/main/scene_tree.cpp11
-rw-r--r--scene/main/scene_tree.h1
-rw-r--r--scene/resources/animation.cpp46
-rw-r--r--scene/resources/importer_mesh.cpp16
-rw-r--r--scene/resources/resource_format_text.cpp3
-rw-r--r--scene/resources/skeleton_modification_2d_twoboneik.cpp4
-rw-r--r--scene/resources/sky_material.cpp34
-rw-r--r--scene/resources/sky_material.h5
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/text_line.cpp23
-rw-r--r--scene/resources/text_line.h4
-rw-r--r--scene/resources/text_paragraph.cpp25
-rw-r--r--scene/resources/text_paragraph.h4
-rw-r--r--scene/resources/tile_set.compat.inc48
-rw-r--r--scene/resources/tile_set.cpp151
-rw-r--r--scene/resources/tile_set.h29
-rw-r--r--scene/resources/visual_shader.cpp13
33 files changed, 493 insertions, 207 deletions
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index bda7b495e1..ee186de5f1 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -101,7 +101,12 @@ void Polygon2D::_skeleton_bone_setup_changed() {
}
void Polygon2D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED && !Engine::get_singleton()->is_editor_hint()) {
+ return; // Mesh recreation for NOTIFICATION_TRANSFORM_CHANGED is only needed in editor.
+ }
+
switch (p_what) {
+ case NOTIFICATION_TRANSFORM_CHANGED:
case NOTIFICATION_DRAW: {
if (polygon.size() < 3) {
return;
@@ -364,7 +369,30 @@ void Polygon2D::_notification(int p_what) {
arr[RS::ARRAY_INDEX] = index_array;
- RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+ RS::SurfaceData sd;
+
+ if (skeleton_node) {
+ // Compute transform between mesh and skeleton for runtime AABB compute.
+ const Transform2D mesh_transform = get_global_transform();
+ const Transform2D skeleton_transform = skeleton_node->get_global_transform();
+ const Transform2D mesh_to_sk2d = mesh_transform * skeleton_transform.affine_inverse();
+
+ // Convert 2d transform to 3d.
+ sd.mesh_to_skeleton_xform.basis.rows[0][0] = mesh_to_sk2d.columns[0][0];
+ sd.mesh_to_skeleton_xform.basis.rows[0][1] = mesh_to_sk2d.columns[0][1];
+ sd.mesh_to_skeleton_xform.origin.x = mesh_to_sk2d.get_origin().x;
+
+ sd.mesh_to_skeleton_xform.basis.rows[1][0] = mesh_to_sk2d.columns[1][0];
+ sd.mesh_to_skeleton_xform.basis.rows[1][1] = mesh_to_sk2d.columns[1][1];
+ sd.mesh_to_skeleton_xform.origin.y = mesh_to_sk2d.get_origin().y;
+ }
+
+ Error err = RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
+ if (err != OK) {
+ return;
+ }
+
+ RS::get_singleton()->mesh_add_surface(mesh, sd);
RS::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh, Transform2D(), Color(1, 1, 1), texture.is_valid() ? texture->get_rid() : RID());
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 04e362c426..f2be91cbf3 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -447,11 +447,12 @@ void TileMapLayer::_rendering_update() {
for (KeyValue<Vector2i, CellData> &kv : tile_map) {
CellData &cell_data = kv.value;
for (const RID &occluder : cell_data.occluders) {
- if (occluder.is_valid()) {
- Transform2D xform(0, tile_map_node->map_to_local(kv.key));
- rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas());
- rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform);
+ if (occluder.is_null()) {
+ continue;
}
+ Transform2D xform(0, tile_map_node->map_to_local(kv.key));
+ rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas());
+ rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform);
}
}
}
@@ -591,6 +592,11 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile);
}
+ // Transform flags.
+ bool flip_h = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
+ bool flip_v = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
+ bool transpose = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+
// Create, update or clear occluders.
for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) {
Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer_index);
@@ -606,7 +612,7 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
}
rs->canvas_light_occluder_set_enabled(occluder, node_visible);
rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform);
- rs->canvas_light_occluder_set_polygon(occluder, tile_map_node->get_transformed_polygon(Ref<Resource>(tile_data->get_occluder(occlusion_layer_index)), r_cell_data.cell.alternative_tile)->get_rid());
+ rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid());
rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas());
rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index));
} else {
@@ -800,6 +806,11 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
+ // Transform flags.
+ bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
+ bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
+ bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+
// Free unused bodies then resize the bodies array.
for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) {
RID body = r_cell_data.bodies[i];
@@ -864,8 +875,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index);
for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
// Add decomposed convex shapes.
- Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index);
- shape = tile_map_node->get_transformed_polygon(Ref<Resource>(shape), c.alternative_tile);
+ Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index, flip_h, flip_v, transpose);
ps->body_add_shape(body, shape->get_rid());
ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin);
@@ -1053,6 +1063,11 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
+ // Transform flags.
+ bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
+ bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
+ bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+
// Free unused regions then resize the regions array.
for (uint32_t i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) {
RID &region = r_cell_data.navigation_regions[i];
@@ -1066,9 +1081,7 @@ void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) {
// Create, update or clear regions.
for (uint32_t navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) {
- Ref<NavigationPolygon> navigation_polygon;
- navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index);
- navigation_polygon = tile_map_node->get_transformed_polygon(Ref<Resource>(navigation_polygon), c.alternative_tile);
+ Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index, flip_h, flip_v, transpose);
RID &region = r_cell_data.navigation_regions[navigation_layer_index];
@@ -1161,9 +1174,11 @@ void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const V
rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant);
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
- Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index);
+ bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H);
+ bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V);
+ bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
+ Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index, flip_h, flip_v, transpose);
if (navigation_polygon.is_valid()) {
- navigation_polygon = tile_map_node->get_transformed_polygon(Ref<Resource>(navigation_polygon), c.alternative_tile);
Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices();
if (navigation_polygon_vertices.size() < 3) {
continue;
@@ -3119,11 +3134,6 @@ void TileMap::_internal_update() {
return;
}
- // FIXME: This should only clear polygons that are no longer going to be used, but since it's difficult to determine,
- // the cache is never cleared at runtime to prevent invalidating used polygons.
- if (Engine::get_singleton()->is_editor_hint()) {
- polygon_cache.clear();
- }
// Update dirty quadrants on layers.
for (Ref<TileMapLayer> &layer : layers) {
layer->internal_update();
@@ -3608,37 +3618,6 @@ Rect2 TileMap::_edit_get_rect() const {
}
#endif
-PackedVector2Array TileMap::_get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id) {
- const Vector2 *r = p_vertices.ptr();
- int size = p_vertices.size();
-
- PackedVector2Array new_points;
- new_points.resize(size);
- Vector2 *w = new_points.ptrw();
-
- bool flip_h = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
- bool flip_v = (p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
- bool transpose = (p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
-
- for (int i = 0; i < size; i++) {
- Vector2 v;
- if (transpose) {
- v = Vector2(r[i].y, r[i].x);
- } else {
- v = r[i];
- }
-
- if (flip_h) {
- v.x *= -1;
- }
- if (flip_v) {
- v.y *= -1;
- }
- w[i] = v;
- }
- return new_points;
-}
-
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (p_name == "format") {
@@ -4637,57 +4616,6 @@ void TileMap::draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_ce
#undef DRAW_SIDE_IF_NEEDED
}
-Ref<Resource> TileMap::get_transformed_polygon(Ref<Resource> p_polygon, int p_alternative_id) {
- if (!bool(p_alternative_id & (TileSetAtlasSource::TRANSFORM_FLIP_H | TileSetAtlasSource::TRANSFORM_FLIP_V | TileSetAtlasSource::TRANSFORM_TRANSPOSE))) {
- return p_polygon;
- }
-
- {
- HashMap<Pair<Ref<Resource>, int>, Ref<Resource>, PairHash<Ref<Resource>, int>>::Iterator E = polygon_cache.find(Pair<Ref<Resource>, int>(p_polygon, p_alternative_id));
- if (E) {
- return E->value;
- }
- }
-
- Ref<ConvexPolygonShape2D> col = p_polygon;
- if (col.is_valid()) {
- Ref<ConvexPolygonShape2D> ret;
- ret.instantiate();
- ret->set_points(_get_transformed_vertices(col->get_points(), p_alternative_id));
- polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
- return ret;
- }
-
- Ref<NavigationPolygon> nav = p_polygon;
- if (nav.is_valid()) {
- PackedVector2Array new_points = _get_transformed_vertices(nav->get_vertices(), p_alternative_id);
- Ref<NavigationPolygon> ret;
- ret.instantiate();
- ret->set_vertices(new_points);
-
- PackedInt32Array indices;
- indices.resize(new_points.size());
- int *w = indices.ptrw();
- for (int i = 0; i < new_points.size(); i++) {
- w[i] = i;
- }
- ret->add_polygon(indices);
- polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
- return ret;
- }
-
- Ref<OccluderPolygon2D> ocd = p_polygon;
- if (ocd.is_valid()) {
- Ref<OccluderPolygon2D> ret;
- ret.instantiate();
- ret->set_polygon(_get_transformed_vertices(ocd->get_polygon(), p_alternative_id));
- polygon_cache[Pair<Ref<Resource>, int>(p_polygon, p_alternative_id)] = ret;
- return ret;
- }
-
- return p_polygon;
-}
-
PackedStringArray TileMap::get_configuration_warnings() const {
PackedStringArray warnings = Node::get_configuration_warnings();
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 9a847a7ebb..1e4c6d0e66 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -472,10 +472,6 @@ private:
void _update_notify_local_transform();
- // Polygons.
- HashMap<Pair<Ref<Resource>, int>, Ref<Resource>, PairHash<Ref<Resource>, int>> polygon_cache;
- PackedVector2Array _get_transformed_vertices(const PackedVector2Array &p_vertices, int p_alternative_id);
-
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -619,7 +615,6 @@ public:
// Helpers?
TypedArray<Vector2i> get_surrounding_cells(const Vector2i &coords);
void draw_cells_outline(Control *p_control, const RBSet<Vector2i> &p_cells, Color p_color, Transform2D p_transform = Transform2D());
- Ref<Resource> get_transformed_polygon(Ref<Resource> p_polygon, int p_alternative_id);
// Virtual function to modify the TileData at runtime.
GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index c6d69cf622..da00d1dd7b 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -638,6 +638,10 @@ bool AnimationMixer::_update_caches() {
switch (track_type) {
case Animation::TYPE_VALUE: {
+ // If a value track without a key is cached first, the initial value cannot be determined.
+ // It is a corner case, but which may cause problems with blending.
+ ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' must have at least one key to cache for blending.");
+
TrackCacheValue *track_value = memnew(TrackCacheValue);
if (resource.is_valid()) {
@@ -654,9 +658,6 @@ bool AnimationMixer::_update_caches() {
track = track_value;
- // If a value track without a key is cached first, the initial value cannot be determined.
- // It is a corner case, but which may cause problems with blending.
- ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' must have at least one key to cache for blending.");
track_value->init_value = anim->track_get_key_value(i, 0);
track_value->init_value.zero();
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index dd5ff7912f..f4e1b3615c 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -860,7 +860,7 @@ double AnimationNodeStateMachinePlayback::_process(const String &p_base_path, An
pi.time = 0;
pi.seeked = true;
}
- fading_from_rem = p_state_machine->blend_node(p_state_machine->states[fading_from].node, fading_from, pi, AnimationNode::FILTER_IGNORE, true); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
+ fading_from_rem = p_state_machine->blend_node(p_state_machine->states[fading_from].node, fading_from, pi, AnimationNode::FILTER_IGNORE, true, p_test_only); // Blend values must be more than CMP_EPSILON to process discrete keys in edge.
// Guess playback position.
if (fading_from_rem > len_fade_from) { /// Weird but ok.
@@ -976,7 +976,7 @@ bool AnimationNodeStateMachinePlayback::_transition_to_next_recursive(AnimationT
pi.seeked = true;
pi.is_external_seeking = false;
pi.weight = 0;
- p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true);
+ p_state_machine->blend_node(p_state_machine->states[current].node, current, pi, AnimationNode::FILTER_IGNORE, true, p_test_only);
}
// Just get length to find next recursive.
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index bdadc4ecac..e24816122c 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -100,9 +100,9 @@ void AnimationNode::blend_animation(const StringName &p_animation, AnimationMixe
process_state->tree->make_animation_instance(p_animation, p_playback_info);
}
-double AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info) {
+double AnimationNode::_pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) {
process_state = p_process_state;
- double t = process(p_playback_info);
+ double t = process(p_playback_info, p_test_only);
process_state = nullptr;
return t;
}
@@ -152,7 +152,7 @@ double AnimationNode::blend_input(int p_input, AnimationMixer::PlaybackInfo p_pl
}
double AnimationNode::blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter, bool p_sync, bool p_test_only) {
- node_state.connections.clear();
+ p_node->node_state.connections.clear();
return _blend_node(p_node, p_subpath, this, p_playback_info, p_filter, p_sync, p_test_only, nullptr);
}
@@ -269,9 +269,9 @@ double AnimationNode::_blend_node(Ref<AnimationNode> p_node, const StringName &p
p_node->node_state.parent = new_parent;
if (!p_playback_info.seeked && !p_sync && !any_valid) {
p_playback_info.time = 0.0;
- return p_node->_pre_process(process_state, p_playback_info);
+ return p_node->_pre_process(process_state, p_playback_info, p_test_only);
}
- return p_node->_pre_process(process_state, p_playback_info);
+ return p_node->_pre_process(process_state, p_playback_info, p_test_only);
}
String AnimationNode::get_caption() const {
@@ -565,12 +565,12 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
if (started) {
// If started, seek.
pi.seeked = true;
- root_animation_node->_pre_process(&process_state, pi);
+ root_animation_node->_pre_process(&process_state, pi, false);
started = false;
- } else {
- pi.time = p_delta;
- root_animation_node->_pre_process(&process_state, pi);
}
+ pi.seeked = false;
+ pi.time = p_delta;
+ root_animation_node->_pre_process(&process_state, pi, false);
}
if (!process_state.valid) {
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 0be487d3fd..87928e4d20 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -85,7 +85,7 @@ public:
void _set_filters(const Array &p_filters);
friend class AnimationNodeBlendTree;
double _blend_node(Ref<AnimationNode> p_node, const StringName &p_subpath, AnimationNode *p_new_parent, AnimationMixer::PlaybackInfo p_playback_info, FilterAction p_filter = FILTER_IGNORE, bool p_sync = true, bool p_test_only = false, real_t *r_activity = nullptr);
- double _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info);
+ double _pre_process(ProcessState *p_process_state, AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
protected:
virtual double _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index f250662be0..23b216195a 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -74,7 +74,7 @@ void ColorPicker::_notification(int p_what) {
sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
- alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
+ alpha_slider->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
mode_btns[i]->begin_bulk_theme_override();
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 2fbd29b048..3df0d97160 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -240,10 +240,12 @@ void Label::_shape() {
if (i < jst_to_line) {
TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags);
} else if (i == (visible_lines - 1)) {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
}
}
} else if (lines_hidden) {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[visible_lines - 1], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
}
} else {
@@ -268,9 +270,11 @@ void Label::_shape() {
if (i < jst_to_line && horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags);
overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
+ TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
} else {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
}
}
@@ -887,6 +891,27 @@ TextServer::OverrunBehavior Label::get_text_overrun_behavior() const {
return overrun_behavior;
}
+void Label::set_ellipsis_char(const String &p_char) {
+ String c = p_char;
+ if (c.length() > 1) {
+ WARN_PRINT("Ellipsis must be exactly one character long (" + itos(c.length()) + " characters given).");
+ c = c.left(1);
+ }
+ if (el_char == c) {
+ return;
+ }
+ el_char = c;
+ lines_dirty = true;
+ queue_redraw();
+ if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
+ update_minimum_size();
+ }
+}
+
+String Label::get_ellipsis_char() const {
+ return el_char;
+}
+
String Label::get_text() const {
return text;
}
@@ -1007,6 +1032,8 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_stops"), &Label::get_tab_stops);
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &Label::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Label::get_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("set_ellipsis_char", "char"), &Label::set_ellipsis_char);
+ ClassDB::bind_method(D_METHOD("get_ellipsis_char"), &Label::get_ellipsis_char);
ClassDB::bind_method(D_METHOD("set_uppercase", "enable"), &Label::set_uppercase);
ClassDB::bind_method(D_METHOD("is_uppercase"), &Label::is_uppercase);
ClassDB::bind_method(D_METHOD("get_line_height", "line"), &Label::get_line_height, DEFVAL(-1));
@@ -1037,6 +1064,7 @@ void Label::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "ellipsis_char"), "set_ellipsis_char", "get_ellipsis_char");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "tab_stops"), "set_tab_stops", "get_tab_stops");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index a5126c6b91..44443e3eb4 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -45,6 +45,7 @@ private:
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
bool clip = false;
+ String el_char = U"…";
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
Size2 minsize;
bool uppercase = false;
@@ -147,6 +148,9 @@ public:
void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior);
TextServer::OverrunBehavior get_text_overrun_behavior() const;
+ void set_ellipsis_char(const String &p_char);
+ String get_ellipsis_char() const;
+
void set_lines_skipped(int p_lines);
int get_lines_skipped() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5ed1a9d5e3..5cca09bcf6 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -1914,12 +1914,15 @@ bool LineEdit::is_secret() const {
}
void LineEdit::set_secret_character(const String &p_string) {
- if (secret_character == p_string) {
+ String c = p_string;
+ if (c.length() > 1) {
+ WARN_PRINT("Secret character must be exactly one character long (" + itos(c.length()) + " characters given).");
+ c = c.left(1);
+ }
+ if (secret_character == c) {
return;
}
-
- secret_character = p_string;
- update_configuration_warnings();
+ secret_character = c;
_shape();
queue_redraw();
}
@@ -2285,14 +2288,8 @@ void LineEdit::_shape() {
if (text.length() == 0 && ime_text.length() == 0) {
t = placeholder_translated;
} else if (pass) {
- // TODO: Integrate with text server to add support for non-latin scripts.
- // Allow secret_character as empty strings, act like if a space was used as a secret character.
- String secret = " ";
- // Allow values longer than 1 character in the property, but trim characters after the first one.
- if (!secret_character.is_empty()) {
- secret = secret_character.left(1);
- }
- t = secret.repeat(text.length() + ime_text.length());
+ String s = (secret_character.length() > 0) ? secret_character.left(1) : U"•";
+ t = s.repeat(text.length() + ime_text.length());
} else {
if (ime_text.length() > 0) {
t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length());
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 41f4de5b3b..fbc374e89e 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -6331,11 +6331,9 @@ Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_id
Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
Dictionary d;
for (int i = 0; i < p_expressions.size(); i++) {
- String expression = p_expressions[i];
-
Array a;
- Vector<String> parts = expression.split("=", true);
- String key = parts[0];
+ Vector<String> parts = p_expressions[i].split("=", true);
+ const String &key = parts[0];
if (parts.size() != 2) {
return d;
}
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 777ca96cc4..e9fbdbb312 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -1664,7 +1664,7 @@ bool TabBar::_set(const StringName &p_name, const Variant &p_value) {
} else if (property == "icon") {
set_tab_icon(tab_index, p_value);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
set_tab_disabled(tab_index, p_value);
return true;
}
@@ -1683,7 +1683,7 @@ bool TabBar::_get(const StringName &p_name, Variant &r_ret) const {
} else if (property == "icon") {
r_ret = get_tab_icon(tab_index);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
r_ret = is_tab_disabled(tab_index);
return true;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 5817f70343..308250c592 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5314,8 +5314,7 @@ int TextEdit::get_line_wrap_index_at_column(int p_line, int p_column) const {
Vector<String> lines = get_line_wrapped_text(p_line);
for (int i = 0; i < lines.size(); i++) {
wrap_index = i;
- String s = lines[wrap_index];
- col += s.length();
+ col += lines[wrap_index].length();
if (col > p_column) {
break;
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 1d06ce05ae..8a243fd68f 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -70,19 +70,27 @@ void TreeItem::Cell::draw_icon(const RID &p_where, const Point2 &p_pos, const Si
}
void TreeItem::_changed_notify(int p_cell) {
- tree->item_changed(p_cell, this);
+ if (tree) {
+ tree->item_changed(p_cell, this);
+ }
}
void TreeItem::_changed_notify() {
- tree->item_changed(-1, this);
+ if (tree) {
+ tree->item_changed(-1, this);
+ }
}
void TreeItem::_cell_selected(int p_cell) {
- tree->item_selected(p_cell, this);
+ if (tree) {
+ tree->item_selected(p_cell, this);
+ }
}
void TreeItem::_cell_deselected(int p_cell) {
- tree->item_deselected(p_cell, this);
+ if (tree) {
+ tree->item_deselected(p_cell, this);
+ }
}
void TreeItem::_change_tree(Tree *p_tree) {
diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp
index de42b63548..fe0e82370a 100644
--- a/scene/main/resource_preloader.cpp
+++ b/scene/main/resource_preloader.cpp
@@ -40,12 +40,11 @@ void ResourcePreloader::_set_resources(const Array &p_data) {
ERR_FAIL_COND(names.size() != resdata.size());
for (int i = 0; i < resdata.size(); i++) {
- String name = names[i];
Ref<Resource> resource = resdata[i];
ERR_CONTINUE(!resource.is_valid());
- resources[name] = resource;
+ resources[names[i]] = resource;
- //add_resource(name,resource);
+ //add_resource(names[i],resource);
}
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index cf80bd6c6f..4417007b9d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1302,6 +1302,16 @@ bool SceneTree::has_group(const StringName &p_identifier) const {
return group_map.has(p_identifier);
}
+int SceneTree::get_node_count_in_group(const StringName &p_group) const {
+ _THREAD_SAFE_METHOD_
+ HashMap<StringName, Group>::ConstIterator E = group_map.find(p_group);
+ if (!E) {
+ return 0;
+ }
+
+ return E->value.nodes.size();
+}
+
Node *SceneTree::get_first_node_in_group(const StringName &p_group) {
_THREAD_SAFE_METHOD_
HashMap<StringName, Group>::Iterator E = group_map.find(p_group);
@@ -1617,6 +1627,7 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_nodes_in_group", "group"), &SceneTree::_get_nodes_in_group);
ClassDB::bind_method(D_METHOD("get_first_node_in_group", "group"), &SceneTree::get_first_node_in_group);
+ ClassDB::bind_method(D_METHOD("get_node_count_in_group", "group"), &SceneTree::get_node_count_in_group);
ClassDB::bind_method(D_METHOD("set_current_scene", "child_node"), &SceneTree::set_current_scene);
ClassDB::bind_method(D_METHOD("get_current_scene"), &SceneTree::get_current_scene);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index e1597d3890..618ac275ff 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -385,6 +385,7 @@ public:
void get_nodes_in_group(const StringName &p_group, List<Node *> *p_list);
Node *get_first_node_in_group(const StringName &p_group);
bool has_group(const StringName &p_identifier) const;
+ int get_node_count_in_group(const StringName &p_group) const;
//void change_scene(const String& p_path);
//Node *get_loaded_scene();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index b1b3bab937..52850cac4a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -5485,7 +5485,7 @@ Variant Animation::cast_to_blendwise(const Variant p_value) {
switch (p_value.get_type()) {
case Variant::BOOL:
case Variant::INT: {
- return p_value.operator real_t();
+ return p_value.operator double();
} break;
case Variant::STRING:
case Variant::STRING_NAME: {
@@ -5521,7 +5521,7 @@ Variant Animation::cast_from_blendwise(const Variant p_value, const Variant::Typ
return p_value.operator real_t() >= 0.5;
} break;
case Variant::INT: {
- return (int)Math::round(p_value.operator real_t());
+ return (int64_t)Math::round(p_value.operator double());
} break;
case Variant::STRING: {
return array_to_string(p_value);
@@ -5594,8 +5594,12 @@ Variant Animation::array_to_string(const Variant p_value) {
}
Variant Animation::add_variant(const Variant &a, const Variant &b) {
- if (a.get_type() != b.get_type() && !a.is_array()) {
- return a;
+ if (a.get_type() != b.get_type()) {
+ if (a.is_num() && b.is_num()) {
+ return add_variant(cast_to_blendwise(a), cast_to_blendwise(b));
+ } else if (!a.is_array()) {
+ return a;
+ }
}
switch (a.get_type()) {
@@ -5603,7 +5607,7 @@ Variant Animation::add_variant(const Variant &a, const Variant &b) {
return Variant();
} break;
case Variant::FLOAT: {
- return (a.operator real_t()) + (b.operator real_t());
+ return (a.operator double()) + (b.operator double());
} break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
@@ -5704,8 +5708,12 @@ Variant Animation::add_variant(const Variant &a, const Variant &b) {
}
Variant Animation::subtract_variant(const Variant &a, const Variant &b) {
- if (a.get_type() != b.get_type() && !a.is_array()) {
- return a;
+ if (a.get_type() != b.get_type()) {
+ if (a.is_num() && b.is_num()) {
+ return subtract_variant(cast_to_blendwise(a), cast_to_blendwise(b));
+ } else if (!a.is_array()) {
+ return a;
+ }
}
switch (a.get_type()) {
@@ -5713,7 +5721,7 @@ Variant Animation::subtract_variant(const Variant &a, const Variant &b) {
return Variant();
} break;
case Variant::FLOAT: {
- return (a.operator real_t()) - (b.operator real_t());
+ return (a.operator double()) - (b.operator double());
} break;
case Variant::RECT2: {
const Rect2 ra = a.operator Rect2();
@@ -5814,8 +5822,12 @@ Variant Animation::subtract_variant(const Variant &a, const Variant &b) {
}
Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) {
- if (a.get_type() != b.get_type() && !a.is_array()) {
- return a;
+ if (a.get_type() != b.get_type()) {
+ if (a.is_num() && b.is_num()) {
+ return blend_variant(cast_to_blendwise(a), cast_to_blendwise(b), c);
+ } else if (!a.is_array()) {
+ return a;
+ }
}
switch (a.get_type()) {
@@ -5823,7 +5835,7 @@ Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) {
return Variant();
} break;
case Variant::FLOAT: {
- return (a.operator real_t()) + (b.operator real_t()) * c;
+ return (a.operator double()) + (b.operator double()) * c;
} break;
case Variant::VECTOR2: {
return (a.operator Vector2()) + (b.operator Vector2()) * c;
@@ -5947,8 +5959,12 @@ Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) {
}
Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float c, bool p_snap_array_element) {
- if (a.get_type() != b.get_type() && !a.is_array()) {
- return a;
+ if (a.get_type() != b.get_type()) {
+ if (a.is_num() && b.is_num()) {
+ return interpolate_variant(cast_to_blendwise(a), cast_to_blendwise(b), c);
+ } else if (!a.is_array()) {
+ return a;
+ }
}
switch (a.get_type()) {
@@ -5956,8 +5972,8 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
return Variant();
} break;
case Variant::FLOAT: {
- const real_t va = a.operator real_t();
- return va + ((b.operator real_t()) - va) * c;
+ const double va = a.operator double();
+ return va + ((b.operator double()) - va) * c;
} break;
case Variant::VECTOR2: {
return (a.operator Vector2()).lerp(b.operator Vector2(), c);
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index 66d758080e..2ffb8da46c 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -418,11 +418,10 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
}
}
- LocalVector<float> normal_weights;
- normal_weights.resize(merged_vertex_count);
- for (unsigned int j = 0; j < merged_vertex_count; j++) {
- normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
- }
+ const float normal_weights[3] = {
+ // Give some weight to normal preservation, may be worth exposing as an import setting
+ 2.0f, 2.0f, 2.0f
+ };
Vector<float> merged_vertices_f32 = vector3_to_float32_array(merged_vertices_ptr, merged_vertex_count);
float scale = SurfaceTool::simplify_scale_func(merged_vertices_f32.ptr(), merged_vertex_count, sizeof(float) * 3);
@@ -460,12 +459,13 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
(const uint32_t *)merged_indices_ptr, index_count,
merged_vertices_f32.ptr(), merged_vertex_count,
sizeof(float) * 3, // Vertex stride
+ merged_normals_f32.ptr(),
+ sizeof(float) * 3, // Attribute stride
+ normal_weights, 3,
index_target,
max_mesh_error,
simplify_options,
- &mesh_error,
- merged_normals_f32.ptr(),
- normal_weights.ptr(), 3);
+ &mesh_error);
if (new_index_count < last_index_count * 1.5f) {
index_target = index_target * 1.5f;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 19718f12be..3dc920c4be 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -973,8 +973,7 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String
}
if (p_map.has(path)) {
- String np = p_map[path];
- path = np;
+ path = p_map[path];
}
if (relative) {
diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp
index d0b5625298..4458cdc0e3 100644
--- a/scene/resources/skeleton_modification_2d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp
@@ -468,8 +468,8 @@ void SkeletonModification2DTwoBoneIK::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_joint_two_bone_idx"), &SkeletonModification2DTwoBoneIK::get_joint_two_bone_idx);
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_nodepath", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node2D"), "set_target_node", "get_target_node");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0,100000000,0.01,suffix:m"), "set_target_minimum_distance", "get_target_minimum_distance");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0,100000000,0.01,suffix:m"), "set_target_maximum_distance", "get_target_maximum_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_minimum_distance", PROPERTY_HINT_RANGE, "0,100000000,0.01,suffix:px"), "set_target_minimum_distance", "get_target_minimum_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "target_maximum_distance", PROPERTY_HINT_NONE, "0,100000000,0.01,suffix:px"), "set_target_maximum_distance", "get_target_maximum_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_bend_direction", PROPERTY_HINT_NONE, ""), "set_flip_bend_direction", "get_flip_bend_direction");
ADD_GROUP("", "");
}
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 670ce152bd..640261d615 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -161,6 +161,15 @@ bool ProceduralSkyMaterial::get_use_debanding() const {
return use_debanding;
}
+void ProceduralSkyMaterial::set_energy_multiplier(float p_multiplier) {
+ global_energy_multiplier = p_multiplier;
+ RS::get_singleton()->material_set_param(_get_material(), "exposure", global_energy_multiplier);
+}
+
+float ProceduralSkyMaterial::get_energy_multiplier() const {
+ return global_energy_multiplier;
+}
+
Shader::Mode ProceduralSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -226,6 +235,9 @@ void ProceduralSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &ProceduralSkyMaterial::set_use_debanding);
ClassDB::bind_method(D_METHOD("get_use_debanding"), &ProceduralSkyMaterial::get_use_debanding);
+ ClassDB::bind_method(D_METHOD("set_energy_multiplier", "multiplier"), &ProceduralSkyMaterial::set_energy_multiplier);
+ ClassDB::bind_method(D_METHOD("get_energy_multiplier"), &ProceduralSkyMaterial::get_energy_multiplier);
+
ADD_GROUP("Sky", "sky_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color");
@@ -246,6 +258,7 @@ void ProceduralSkyMaterial::_bind_methods() {
ADD_GROUP("", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy_multiplier", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_energy_multiplier", "get_energy_multiplier");
}
void ProceduralSkyMaterial::cleanup_shader() {
@@ -280,6 +293,7 @@ uniform float ground_curve : hint_range(0, 1) = 0.02;
uniform float ground_energy = 1.0;
uniform float sun_angle_max = 30.0;
uniform float sun_curve : hint_range(0, 1) = 0.15;
+uniform float exposure : hint_range(0, 128) = 1.0;
void sky() {
float v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));
@@ -334,7 +348,7 @@ void sky() {
vec3 ground = mix(ground_horizon_color.rgb, ground_bottom_color.rgb, clamp(1.0 - pow(1.0 - c, 1.0 / ground_curve), 0.0, 1.0));
ground *= ground_energy;
- COLOR = mix(ground, sky, step(0.0, EYEDIR.y));
+ COLOR = mix(ground, sky, step(0.0, EYEDIR.y)) * exposure;
}
)",
i ? "render_mode use_debanding;" : ""));
@@ -358,6 +372,7 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() {
set_sun_angle_max(30.0);
set_sun_curve(0.15);
set_use_debanding(true);
+ set_energy_multiplier(1.0);
}
ProceduralSkyMaterial::~ProceduralSkyMaterial() {
@@ -393,6 +408,15 @@ bool PanoramaSkyMaterial::is_filtering_enabled() const {
return filter;
}
+void PanoramaSkyMaterial::set_energy_multiplier(float p_multiplier) {
+ energy_multiplier = p_multiplier;
+ RS::get_singleton()->material_set_param(_get_material(), "exposure", energy_multiplier);
+}
+
+float PanoramaSkyMaterial::get_energy_multiplier() const {
+ return energy_multiplier;
+}
+
Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -420,8 +444,12 @@ void PanoramaSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_filtering_enabled", "enabled"), &PanoramaSkyMaterial::set_filtering_enabled);
ClassDB::bind_method(D_METHOD("is_filtering_enabled"), &PanoramaSkyMaterial::is_filtering_enabled);
+ ClassDB::bind_method(D_METHOD("set_energy_multiplier", "multiplier"), &PanoramaSkyMaterial::set_energy_multiplier);
+ ClassDB::bind_method(D_METHOD("get_energy_multiplier"), &PanoramaSkyMaterial::get_energy_multiplier);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_panorama", "get_panorama");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter"), "set_filtering_enabled", "is_filtering_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy_multiplier", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_energy_multiplier", "get_energy_multiplier");
}
Mutex PanoramaSkyMaterial::shader_mutex;
@@ -447,9 +475,10 @@ void PanoramaSkyMaterial::_update_shader() {
shader_type sky;
uniform sampler2D source_panorama : %s, source_color, hint_default_black;
+uniform float exposure : hint_range(0, 128) = 1.0;
void sky() {
- COLOR = texture(source_panorama, SKY_COORDS).rgb;
+ COLOR = texture(source_panorama, SKY_COORDS).rgb * exposure;
}
)",
i ? "filter_linear" : "filter_nearest"));
@@ -460,6 +489,7 @@ void sky() {
}
PanoramaSkyMaterial::PanoramaSkyMaterial() {
+ set_energy_multiplier(1.0);
}
PanoramaSkyMaterial::~PanoramaSkyMaterial() {
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index f570dd998d..4ffb18c6b4 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -53,6 +53,7 @@ private:
float sun_angle_max = 0.0f;
float sun_curve = 0.0f;
bool use_debanding = true;
+ float global_energy_multiplier = 1.0f;
static Mutex shader_mutex;
static RID shader_cache[2];
@@ -103,6 +104,9 @@ public:
void set_use_debanding(bool p_use_debanding);
bool get_use_debanding() const;
+ void set_energy_multiplier(float p_multiplier);
+ float get_energy_multiplier() const;
+
virtual Shader::Mode get_shader_mode() const override;
virtual RID get_shader_rid() const override;
virtual RID get_rid() const override;
@@ -121,6 +125,7 @@ class PanoramaSkyMaterial : public Material {
private:
Ref<Texture2D> panorama;
+ float energy_multiplier = 1.0f;
static Mutex shader_mutex;
static RID shader_cache[2];
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 2a8ad53525..9dfb298b9e 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -86,7 +86,7 @@ public:
static OptimizeVertexCacheFunc optimize_vertex_cache_func;
typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float *r_error);
static SimplifyFunc simplify_func;
- typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float *result_error, const float *attributes, const float *attribute_weights, size_t attribute_count);
+ typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float *result_error);
static SimplifyWithAttribFunc simplify_with_attrib_func;
typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
static SimplifyScaleFunc simplify_scale_func;
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 98527da96b..76c0a28ea4 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -81,6 +81,10 @@ void TextLine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+ ClassDB::bind_method(D_METHOD("set_ellipsis_char", "char"), &TextLine::set_ellipsis_char);
+ ClassDB::bind_method(D_METHOD("get_ellipsis_char"), &TextLine::get_ellipsis_char);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "ellipsis_char"), "set_ellipsis_char", "get_ellipsis_char");
+
ClassDB::bind_method(D_METHOD("get_objects"), &TextLine::get_objects);
ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect);
@@ -137,8 +141,10 @@ void TextLine::_shape() {
if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(rid, width, flags);
overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
+ TS->shaped_text_set_custom_ellipsis(rid, (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
} else {
+ TS->shaped_text_set_custom_ellipsis(rid, (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(rid, width, overrun_flags);
}
} else if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
@@ -306,6 +312,23 @@ TextServer::OverrunBehavior TextLine::get_text_overrun_behavior() const {
return overrun_behavior;
}
+void TextLine::set_ellipsis_char(const String &p_char) {
+ String c = p_char;
+ if (c.length() > 1) {
+ WARN_PRINT("Ellipsis must be exactly one character long (" + itos(c.length()) + " characters given).");
+ c = c.left(1);
+ }
+ if (el_char == c) {
+ return;
+ }
+ el_char = c;
+ dirty = true;
+}
+
+String TextLine::get_ellipsis_char() const {
+ return el_char;
+}
+
void TextLine::set_width(float p_width) {
width = p_width;
if (alignment == HORIZONTAL_ALIGNMENT_FILL || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index cc52d2e6a7..795bdeb15c 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -47,6 +47,7 @@ private:
float width = -1.0;
BitField<TextServer::JustificationFlag> flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ String el_char = U"…";
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_TRIM_ELLIPSIS;
Vector<float> tab_stops;
@@ -90,6 +91,9 @@ public:
void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior);
TextServer::OverrunBehavior get_text_overrun_behavior() const;
+ void set_ellipsis_char(const String &p_char);
+ String get_ellipsis_char() const;
+
void set_width(float p_width);
float get_width() const;
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 147c9044b8..5da47966dd 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -89,6 +89,10 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
+ ClassDB::bind_method(D_METHOD("set_ellipsis_char", "char"), &TextParagraph::set_ellipsis_char);
+ ClassDB::bind_method(D_METHOD("get_ellipsis_char"), &TextParagraph::get_ellipsis_char);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "ellipsis_char"), "set_ellipsis_char", "get_ellipsis_char");
+
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
@@ -252,10 +256,12 @@ void TextParagraph::_shape_lines() {
if (i < jst_to_line) {
TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags);
} else if (i == (visible_lines - 1)) {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[visible_lines - 1], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], line_w, overrun_flags);
}
}
} else if (lines_hidden) {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[visible_lines - 1], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], (visible_lines - 1 <= dropcap_lines) ? (width - h_offset) : width, overrun_flags);
}
} else {
@@ -281,9 +287,11 @@ void TextParagraph::_shape_lines() {
if (i < jst_to_line && alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags);
overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
+ TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[i], line_w, overrun_flags);
TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
} else {
+ TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
TS->shaped_text_overrun_trim_to_width(lines_rid[i], line_w, overrun_flags);
}
}
@@ -501,6 +509,23 @@ TextServer::OverrunBehavior TextParagraph::get_text_overrun_behavior() const {
return overrun_behavior;
}
+void TextParagraph::set_ellipsis_char(const String &p_char) {
+ String c = p_char;
+ if (c.length() > 1) {
+ WARN_PRINT("Ellipsis must be exactly one character long (" + itos(c.length()) + " characters given).");
+ c = c.left(1);
+ }
+ if (el_char == c) {
+ return;
+ }
+ el_char = c;
+ lines_dirty = true;
+}
+
+String TextParagraph::get_ellipsis_char() const {
+ return el_char;
+}
+
void TextParagraph::set_width(float p_width) {
_THREAD_SAFE_METHOD_
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 31fc95ac02..28c69967ac 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -56,6 +56,7 @@ private:
BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND;
BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
+ String el_char = U"…";
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
@@ -112,6 +113,9 @@ public:
void set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior);
TextServer::OverrunBehavior get_text_overrun_behavior() const;
+ void set_ellipsis_char(const String &p_char);
+ String get_ellipsis_char() const;
+
void set_width(float p_width);
float get_width() const;
diff --git a/scene/resources/tile_set.compat.inc b/scene/resources/tile_set.compat.inc
new file mode 100644
index 0000000000..873ae3aa93
--- /dev/null
+++ b/scene/resources/tile_set.compat.inc
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* tile_set.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+#include "tile_set.h"
+
+Ref<NavigationPolygon> TileData::_get_navigation_polygon_bind_compat_84660(int p_layer_id) const {
+ return get_navigation_polygon(p_layer_id, false, false, false);
+}
+
+Ref<OccluderPolygon2D> TileData::_get_occluder_bind_compat_84660(int p_layer_id) const {
+ return get_occluder(p_layer_id, false, false, false);
+}
+
+void TileData::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_navigation_polygon"), &TileData::_get_navigation_polygon_bind_compat_84660);
+ ClassDB::bind_compatibility_method(D_METHOD("get_occluder"), &TileData::_get_occluder_bind_compat_84660);
+}
+
+#endif
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 422cd4fa2c..2de7cce5c7 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "tile_set.h"
+#include "tile_set.compat.inc"
#include "core/io/marshalls.h"
#include "core/math/geometry_2d.h"
@@ -5104,7 +5105,7 @@ void TileData::add_occlusion_layer(int p_to_pos) {
p_to_pos = occluders.size();
}
ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1);
- occluders.insert(p_to_pos, Ref<OccluderPolygon2D>());
+ occluders.insert(p_to_pos, OcclusionLayerTileData());
}
void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) {
@@ -5219,7 +5220,7 @@ void TileData::add_navigation_layer(int p_to_pos) {
p_to_pos = navigation.size();
}
ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1);
- navigation.insert(p_to_pos, Ref<NavigationPolygon>());
+ navigation.insert(p_to_pos, NavigationLayerTileData());
}
void TileData::move_navigation_layer(int p_from_index, int p_to_pos) {
@@ -5365,13 +5366,35 @@ int TileData::get_y_sort_origin() const {
void TileData::set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon) {
ERR_FAIL_INDEX(p_layer_id, occluders.size());
- occluders.write[p_layer_id] = p_occluder_polygon;
+ occluders.write[p_layer_id].occluder = p_occluder_polygon;
+ occluders.write[p_layer_id].transformed_occluders.clear();
emit_signal(SNAME("changed"));
}
-Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
+Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref<OccluderPolygon2D>());
- return occluders[p_layer_id];
+
+ const OcclusionLayerTileData &layer_tile_data = occluders[p_layer_id];
+
+ int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2;
+ if (key == 0) {
+ return layer_tile_data.occluder;
+ }
+
+ if (layer_tile_data.occluder.is_null()) {
+ return Ref<OccluderPolygon2D>();
+ }
+
+ HashMap<int, Ref<OccluderPolygon2D>>::Iterator I = layer_tile_data.transformed_occluders.find(key);
+ if (!I) {
+ Ref<OccluderPolygon2D> transformed_polygon;
+ transformed_polygon.instantiate();
+ transformed_polygon->set_polygon(get_transformed_vertices(layer_tile_data.occluder->get_polygon(), p_flip_h, p_flip_v, p_transpose));
+ layer_tile_data.transformed_occluders[key] = transformed_polygon;
+ return transformed_polygon;
+ } else {
+ return I->value;
+ }
}
// Physics
@@ -5431,22 +5454,25 @@ void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index,
ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size());
ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points.");
+ TileData::PhysicsLayerTileData::PolygonShapeTileData &polygon_shape_tile_data = physics.write[p_layer_id].polygons.write[p_polygon_index];
+
if (p_polygon.is_empty()) {
- physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear();
+ polygon_shape_tile_data.shapes.clear();
} else {
// Decompose into convex shapes.
Vector<Vector<Vector2>> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon);
ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes.");
- physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size());
+ polygon_shape_tile_data.shapes.resize(decomp.size());
for (int i = 0; i < decomp.size(); i++) {
Ref<ConvexPolygonShape2D> shape;
shape.instantiate();
shape->set_points(decomp[i]);
- physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape;
+ polygon_shape_tile_data.shapes[i] = shape;
}
}
- physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon;
+ polygon_shape_tile_data.transformed_shapes.clear();
+ polygon_shape_tile_data.polygon = p_polygon;
emit_signal(SNAME("changed"));
}
@@ -5488,11 +5514,36 @@ int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_i
return physics[p_layer_id].polygons[p_polygon_index].shapes.size();
}
-Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const {
+Ref<ConvexPolygonShape2D> TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref<ConvexPolygonShape2D>());
ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[p_polygon_index].shapes.size(), Ref<ConvexPolygonShape2D>());
- return physics[p_layer_id].polygons[p_polygon_index].shapes[shape_index];
+
+ const PhysicsLayerTileData &layer_tile_data = physics[p_layer_id];
+ const PhysicsLayerTileData::PolygonShapeTileData &shapes_data = layer_tile_data.polygons[p_polygon_index];
+
+ int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2;
+ if (key == 0) {
+ return shapes_data.shapes[shape_index];
+ }
+ if (shapes_data.shapes[shape_index].is_null()) {
+ return Ref<ConvexPolygonShape2D>();
+ }
+
+ HashMap<int, LocalVector<Ref<ConvexPolygonShape2D>>>::Iterator I = shapes_data.transformed_shapes.find(key);
+ if (!I) {
+ int size = shapes_data.shapes.size();
+ shapes_data.transformed_shapes[key].resize(size);
+ for (int i = 0; i < size; i++) {
+ Ref<ConvexPolygonShape2D> transformed_polygon;
+ transformed_polygon.instantiate();
+ transformed_polygon->set_points(get_transformed_vertices(shapes_data.shapes[shape_index]->get_points(), p_flip_h, p_flip_v, p_transpose));
+ shapes_data.transformed_shapes[key][i] = transformed_polygon;
+ }
+ return shapes_data.transformed_shapes[key][shape_index];
+ } else {
+ return I->value[shape_index];
+ }
}
// Terrain
@@ -5570,13 +5621,50 @@ TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
// Navigation
void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
ERR_FAIL_INDEX(p_layer_id, navigation.size());
- navigation.write[p_layer_id] = p_navigation_polygon;
+ navigation.write[p_layer_id].navigation_polygon = p_navigation_polygon;
+ navigation.write[p_layer_id].transformed_navigation_polygon.clear();
emit_signal(SNAME("changed"));
}
-Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id) const {
+Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id, bool p_flip_h, bool p_flip_v, bool p_transpose) const {
ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref<NavigationPolygon>());
- return navigation[p_layer_id];
+
+ const NavigationLayerTileData &layer_tile_data = navigation[p_layer_id];
+
+ int key = int(p_flip_h) | int(p_flip_v) << 1 | int(p_transpose) << 2;
+ if (key == 0) {
+ return layer_tile_data.navigation_polygon;
+ }
+
+ if (layer_tile_data.navigation_polygon.is_null()) {
+ return Ref<NavigationPolygon>();
+ }
+
+ HashMap<int, Ref<NavigationPolygon>>::Iterator I = layer_tile_data.transformed_navigation_polygon.find(key);
+ if (!I) {
+ Ref<NavigationPolygon> transformed_polygon;
+ transformed_polygon.instantiate();
+
+ PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose);
+ transformed_polygon->set_vertices(new_points);
+
+ for (int i = 0; i < layer_tile_data.navigation_polygon->get_outline_count(); i++) {
+ PackedVector2Array new_outline = get_transformed_vertices(layer_tile_data.navigation_polygon->get_outline(i), p_flip_h, p_flip_v, p_transpose);
+ transformed_polygon->add_outline(new_outline);
+ }
+
+ PackedInt32Array indices;
+ indices.resize(new_points.size());
+ int *w = indices.ptrw();
+ for (int i = 0; i < new_points.size(); i++) {
+ w[i] = i;
+ }
+ transformed_polygon->add_polygon(indices);
+ layer_tile_data.transformed_navigation_polygon[key] = transformed_polygon;
+ return transformed_polygon;
+ } else {
+ return I->value;
+ }
}
// Misc
@@ -5615,6 +5703,33 @@ Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const {
return custom_data[p_layer_id];
}
+PackedVector2Array TileData::get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose) {
+ const Vector2 *r = p_vertices.ptr();
+ int size = p_vertices.size();
+
+ PackedVector2Array new_points;
+ new_points.resize(size);
+ Vector2 *w = new_points.ptrw();
+
+ for (int i = 0; i < size; i++) {
+ Vector2 v;
+ if (p_transpose) {
+ v = Vector2(r[i].y, r[i].x);
+ } else {
+ v = r[i];
+ }
+
+ if (p_flip_h) {
+ v.x *= -1;
+ }
+ if (p_flip_v) {
+ v.y *= -1;
+ }
+ w[i] = v;
+ }
+ return new_points;
+}
+
bool TileData::_set(const StringName &p_name, const Variant &p_value) {
#ifndef DISABLE_DEPRECATED
if (p_name == "texture_offset") {
@@ -5845,7 +5960,7 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < occluders.size(); i++) {
// occlusion_layer_%d/polygon
property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT);
- if (!occluders[i].is_valid()) {
+ if (occluders[i].occluder.is_null()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(property_info);
@@ -5901,7 +6016,7 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Navigation", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int i = 0; i < navigation.size(); i++) {
property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/%s", i, PNAME("polygon")), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT);
- if (!navigation[i].is_valid()) {
+ if (navigation[i].navigation_polygon.is_null()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
p_list->push_back(property_info);
@@ -5942,7 +6057,7 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin);
ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder);
- ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
+ ClassDB::bind_method(D_METHOD("get_occluder", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_occluder, DEFVAL(false), DEFVAL(false), DEFVAL(false));
// Physics.
ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity);
@@ -5970,7 +6085,7 @@ void TileData::_bind_methods() {
// Navigation
ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon);
- ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon);
+ ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id", "flip_h", "flip_v", "transpose"), &TileData::get_navigation_polygon, DEFVAL(false), DEFVAL(false), DEFVAL(false));
// Misc.
ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 313c4df65d..a71982cd56 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -815,13 +815,18 @@ private:
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
int y_sort_origin = 0;
- Vector<Ref<OccluderPolygon2D>> occluders;
+ struct OcclusionLayerTileData {
+ Ref<OccluderPolygon2D> occluder;
+ mutable HashMap<int, Ref<OccluderPolygon2D>> transformed_occluders;
+ };
+ Vector<OcclusionLayerTileData> occluders;
// Physics
struct PhysicsLayerTileData {
struct PolygonShapeTileData {
LocalVector<Vector2> polygon;
LocalVector<Ref<ConvexPolygonShape2D>> shapes;
+ mutable HashMap<int, LocalVector<Ref<ConvexPolygonShape2D>>> transformed_shapes;
bool one_way = false;
float one_way_margin = 1.0;
};
@@ -839,7 +844,11 @@ private:
int terrain_peering_bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
// Navigation
- Vector<Ref<NavigationPolygon>> navigation;
+ struct NavigationLayerTileData {
+ Ref<NavigationPolygon> navigation_polygon;
+ mutable HashMap<int, Ref<NavigationPolygon>> transformed_navigation_polygon;
+ };
+ Vector<NavigationLayerTileData> navigation;
// Misc
double probability = 1.0;
@@ -853,6 +862,13 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ Ref<NavigationPolygon> _get_navigation_polygon_bind_compat_84660(int p_layer_id) const;
+ Ref<OccluderPolygon2D> _get_occluder_bind_compat_84660(int p_layer_id) const;
+
+ static void _bind_compatibility_methods();
+#endif
+
public:
// Not exposed.
void set_tile_set(const TileSet *p_tile_set);
@@ -901,7 +917,7 @@ public:
int get_y_sort_origin() const;
void set_occluder(int p_layer_id, Ref<OccluderPolygon2D> p_occluder_polygon);
- Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
+ Ref<OccluderPolygon2D> get_occluder(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Physics
void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
@@ -919,7 +935,7 @@ public:
void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin);
float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const;
int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const;
- Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const;
+ Ref<ConvexPolygonShape2D> get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Terrain
void set_terrain_set(int p_terrain_id);
@@ -934,7 +950,7 @@ public:
// Navigation
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
- Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
+ Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false) const;
// Misc
void set_probability(float p_probability);
@@ -945,6 +961,9 @@ public:
Variant get_custom_data(String p_layer_name) const;
void set_custom_data_by_layer_id(int p_layer_id, Variant p_value);
Variant get_custom_data_by_layer_id(int p_layer_id) const;
+
+ // Polygons.
+ static PackedVector2Array get_transformed_vertices(const PackedVector2Array &p_vertices, bool p_flip_h, bool p_flip_v, bool p_transpose);
};
VARIANT_ENUM_CAST(TileSet::CellNeighbor);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 423ef3867f..9fb6b364a7 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -962,12 +962,13 @@ void VisualShader::remove_node(Type p_type, int p_id) {
for (List<Connection>::Element *E = g->connections.front(); E;) {
List<Connection>::Element *N = E->next();
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- if (E->get().from_node == p_id) {
- g->nodes[E->get().to_node].prev_connected_nodes.erase(p_id);
- g->nodes[E->get().to_node].node->set_input_port_connected(E->get().to_port, false);
- } else if (E->get().to_node == p_id) {
- g->nodes[E->get().from_node].next_connected_nodes.erase(p_id);
+ const VisualShader::Connection &connection = E->get();
+ if (connection.from_node == p_id || connection.to_node == p_id) {
+ if (connection.from_node == p_id) {
+ g->nodes[connection.to_node].prev_connected_nodes.erase(p_id);
+ g->nodes[connection.to_node].node->set_input_port_connected(connection.to_port, false);
+ } else if (connection.to_node == p_id) {
+ g->nodes[connection.from_node].next_connected_nodes.erase(p_id);
}
g->connections.erase(E);
}