summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/gpu_particles_2d.cpp22
-rw-r--r--scene/2d/gpu_particles_2d.h8
-rw-r--r--scene/2d/joint_2d.cpp14
-rw-r--r--scene/2d/navigation_link_2d.h6
-rw-r--r--scene/2d/path_2d.cpp45
-rw-r--r--scene/2d/shape_cast_2d.cpp6
-rw-r--r--scene/2d/shape_cast_2d.h1
-rw-r--r--scene/2d/skeleton_2d.cpp2
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/collision_object_3d.cpp2
-rw-r--r--scene/3d/cpu_particles_3d.cpp2
-rw-r--r--scene/3d/gpu_particles_3d.cpp24
-rw-r--r--scene/3d/gpu_particles_3d.h8
-rw-r--r--scene/3d/navigation_link_3d.h6
-rw-r--r--scene/3d/node_3d.cpp23
-rw-r--r--scene/3d/node_3d.h16
-rw-r--r--scene/3d/remote_transform_3d.cpp4
-rw-r--r--scene/3d/shape_cast_3d.cpp6
-rw-r--r--scene/3d/shape_cast_3d.h1
-rw-r--r--scene/3d/sprite_3d.cpp5
-rw-r--r--scene/3d/visual_instance_3d.cpp34
-rw-r--r--scene/3d/visual_instance_3d.h8
-rw-r--r--scene/3d/xr_nodes.cpp6
-rw-r--r--scene/debugger/scene_debugger.cpp77
-rw-r--r--scene/debugger/scene_debugger.h19
-rw-r--r--scene/gui/base_button.cpp30
-rw-r--r--scene/gui/base_button.h5
-rw-r--r--scene/gui/button.cpp2
-rw-r--r--scene/gui/code_edit.cpp23
-rw-r--r--scene/gui/code_edit.h1
-rw-r--r--scene/gui/color_mode.cpp8
-rw-r--r--scene/gui/color_picker.cpp141
-rw-r--r--scene/gui/color_picker.h27
-rw-r--r--scene/gui/flow_container.cpp41
-rw-r--r--scene/gui/flow_container.h13
-rw-r--r--scene/gui/item_list.cpp5
-rw-r--r--scene/gui/line_edit.cpp29
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp4
-rw-r--r--scene/gui/range.cpp12
-rw-r--r--scene/gui/range.h1
-rw-r--r--scene/gui/rich_text_label.cpp6
-rw-r--r--scene/gui/scroll_bar.h2
-rw-r--r--scene/gui/scroll_container.cpp113
-rw-r--r--scene/gui/tab_container.cpp8
-rw-r--r--scene/gui/tab_container.h2
-rw-r--r--scene/gui/text_edit.cpp36
-rw-r--r--scene/gui/text_edit.h1
-rw-r--r--scene/gui/texture_button.cpp2
-rw-r--r--scene/gui/tree.cpp51
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/main/canvas_item.cpp31
-rw-r--r--scene/main/canvas_item.h7
-rw-r--r--scene/main/canvas_layer.cpp1
-rw-r--r--scene/main/multiplayer_api.cpp2
-rw-r--r--scene/main/multiplayer_peer.cpp7
-rw-r--r--scene/main/multiplayer_peer.h5
-rw-r--r--scene/main/node.cpp14
-rw-r--r--scene/main/scene_tree.cpp21
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/main/viewport.cpp122
-rw-r--r--scene/main/viewport.h9
-rw-r--r--scene/main/window.cpp2
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/animation.cpp86
-rw-r--r--scene/resources/bone_map.cpp2
-rw-r--r--scene/resources/curve.cpp41
-rw-r--r--scene/resources/curve.h1
-rw-r--r--scene/resources/default_theme/color_picker_hue.svg1
-rw-r--r--scene/resources/default_theme/default_theme.cpp55
-rw-r--r--scene/resources/font.cpp221
-rw-r--r--scene/resources/importer_mesh.cpp2
-rw-r--r--scene/resources/mesh.cpp12
-rw-r--r--scene/resources/primitive_meshes.cpp583
-rw-r--r--scene/resources/primitive_meshes.h35
-rw-r--r--scene/resources/resource_format_text.cpp2
-rw-r--r--scene/resources/shader.cpp2
-rw-r--r--scene/resources/shape_2d.cpp2
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.h4
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.h6
-rw-r--r--scene/resources/syntax_highlighter.cpp2
-rw-r--r--scene/resources/text_line.cpp49
-rw-r--r--scene/resources/text_paragraph.cpp86
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--scene/resources/tile_set.cpp2
-rw-r--r--scene/resources/tile_set.h2
-rw-r--r--scene/resources/visual_shader_nodes.cpp4
-rw-r--r--scene/theme/theme_db.cpp4
89 files changed, 1693 insertions, 655 deletions
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 9786b01058..c8f5d7f5a6 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -141,16 +141,16 @@ void GPUParticles2D::set_process_material(const Ref<Material> &p_material) {
void GPUParticles2D::set_trail_enabled(bool p_enabled) {
trail_enabled = p_enabled;
- RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length);
+ RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
queue_redraw();
RS::get_singleton()->particles_set_transform_align(particles, p_enabled ? RS::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY : RS::PARTICLES_TRANSFORM_ALIGN_DISABLED);
}
-void GPUParticles2D::set_trail_length(double p_seconds) {
+void GPUParticles2D::set_trail_lifetime(double p_seconds) {
ERR_FAIL_COND(p_seconds < 0.001);
- trail_length = p_seconds;
- RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length);
+ trail_lifetime = p_seconds;
+ RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
queue_redraw();
}
@@ -181,8 +181,8 @@ bool GPUParticles2D::is_trail_enabled() const {
return trail_enabled;
}
-double GPUParticles2D::get_trail_length() const {
- return trail_length;
+double GPUParticles2D::get_trail_lifetime() const {
+ return trail_lifetime;
}
void GPUParticles2D::_update_collision_size() {
@@ -299,10 +299,6 @@ bool GPUParticles2D::get_interpolate() const {
PackedStringArray GPUParticles2D::get_configuration_warnings() const {
PackedStringArray warnings = Node2D::get_configuration_warnings();
- if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose."));
- }
-
if (process_material.is_null()) {
warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
} else {
@@ -614,10 +610,10 @@ void GPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles2D::emit_particle);
ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles2D::set_trail_enabled);
- ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles2D::set_trail_length);
+ ClassDB::bind_method(D_METHOD("set_trail_lifetime", "secs"), &GPUParticles2D::set_trail_lifetime);
ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles2D::is_trail_enabled);
- ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles2D::get_trail_length);
+ ClassDB::bind_method(D_METHOD("get_trail_lifetime"), &GPUParticles2D::get_trail_lifetime);
ClassDB::bind_method(D_METHOD("set_trail_sections", "sections"), &GPUParticles2D::set_trail_sections);
ClassDB::bind_method(D_METHOD("get_trail_sections"), &GPUParticles2D::get_trail_sections);
@@ -647,7 +643,7 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,Reverse Lifetime"), "set_draw_order", "get_draw_order");
ADD_GROUP("Trails", "trail_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_trail_sections", "get_trail_sections");
ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_section_subdivisions", PROPERTY_HINT_RANGE, "1,1024,1"), "set_trail_section_subdivisions", "get_trail_section_subdivisions");
ADD_GROUP("Process Material", "process_");
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index d613b4ef51..94555e0bb0 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -64,7 +64,7 @@ private:
#endif
Ref<Material> process_material;
- DrawOrder draw_order;
+ DrawOrder draw_order = DRAW_ORDER_LIFETIME;
Ref<Texture2D> texture;
@@ -74,7 +74,7 @@ private:
real_t collision_base_size = 1.0;
bool trail_enabled = false;
- double trail_length = 0.3;
+ double trail_lifetime = 0.3;
int trail_sections = 8;
int trail_section_subdivisions = 4;
@@ -104,7 +104,7 @@ public:
void set_speed_scale(double p_scale);
void set_collision_base_size(real_t p_ratio);
void set_trail_enabled(bool p_enabled);
- void set_trail_length(double p_seconds);
+ void set_trail_lifetime(double p_seconds);
void set_trail_sections(int p_sections);
void set_trail_section_subdivisions(int p_subdivisions);
@@ -126,7 +126,7 @@ public:
real_t get_collision_base_size() const;
bool is_trail_enabled() const;
- double get_trail_length() const;
+ double get_trail_lifetime() const;
int get_trail_sections() const;
int get_trail_section_subdivisions() const;
diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp
index 6000508f36..8de4c281f4 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -133,7 +133,13 @@ void Joint2D::set_node_a(const NodePath &p_node_a) {
}
a = p_node_a;
- _update_joint();
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // When in editor, the setter may be called as a result of node rename.
+ // It happens before the node actually changes its name, which triggers false warning.
+ callable_mp(this, &Joint2D::_update_joint).call_deferred();
+ } else {
+ _update_joint();
+ }
}
NodePath Joint2D::get_node_a() const {
@@ -150,7 +156,11 @@ void Joint2D::set_node_b(const NodePath &p_node_b) {
}
b = p_node_b;
- _update_joint();
+ if (Engine::get_singleton()->is_editor_hint()) {
+ callable_mp(this, &Joint2D::_update_joint).call_deferred();
+ } else {
+ _update_joint();
+ }
}
NodePath Joint2D::get_node_b() const {
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 2a5092216d..9d36f80dd6 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -37,11 +37,11 @@ class NavigationLink2D : public Node2D {
GDCLASS(NavigationLink2D, Node2D);
bool enabled = true;
- RID link = RID();
+ RID link;
bool bidirectional = true;
uint32_t navigation_layers = 1;
- Vector2 end_location = Vector2();
- Vector2 start_location = Vector2();
+ Vector2 end_location;
+ Vector2 start_location;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index c1044fdf5b..b5945a4562 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -175,51 +175,18 @@ void PathFollow2D::_update_transform() {
if (path_length == 0) {
return;
}
- Vector2 pos = c->sample_baked(progress, cubic);
if (rotates) {
- real_t ahead = progress + lookahead;
-
- if (loop && ahead >= path_length) {
- // If our lookahead will loop, we need to check if the path is closed.
- int point_count = c->get_point_count();
- if (point_count > 0) {
- Vector2 start_point = c->get_point_position(0);
- Vector2 end_point = c->get_point_position(point_count - 1);
- if (start_point == end_point) {
- // Since the path is closed we want to 'smooth off'
- // the corner at the start/end.
- // So we wrap the lookahead back round.
- ahead = Math::fmod(ahead, path_length);
- }
- }
- }
-
- Vector2 ahead_pos = c->sample_baked(ahead, cubic);
-
- Vector2 tangent_to_curve;
- if (ahead_pos == pos) {
- // This will happen at the end of non-looping or non-closed paths.
- // We'll try a look behind instead, in order to get a meaningful angle.
- tangent_to_curve =
- (pos - c->sample_baked(progress - lookahead, cubic)).normalized();
- } else {
- tangent_to_curve = (ahead_pos - pos).normalized();
- }
-
- Vector2 normal_of_curve = -tangent_to_curve.orthogonal();
-
- pos += tangent_to_curve * h_offset;
- pos += normal_of_curve * v_offset;
-
- set_rotation(tangent_to_curve.angle());
-
+ Transform2D xform = c->sample_baked_with_rotation(progress, cubic, loop, lookahead);
+ xform.translate_local(v_offset, h_offset);
+ set_rotation(xform[1].angle());
+ set_position(xform[2]);
} else {
+ Vector2 pos = c->sample_baked(progress, cubic);
pos.x += h_offset;
pos.y += v_offset;
+ set_position(pos);
}
-
- set_position(pos);
}
void PathFollow2D::_notification(int p_what) {
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index 6222b0db14..4c2e9e4c19 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -107,6 +107,11 @@ Object *ShapeCast2D::get_collider(int p_idx) const {
return ObjectDB::get_instance(result[p_idx].collider_id);
}
+RID ShapeCast2D::get_collider_rid(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), RID(), "No collider RID found.");
+ return result[p_idx].rid;
+}
+
int ShapeCast2D::get_collider_shape(int p_idx) const {
ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found.");
return result[p_idx].shape;
@@ -422,6 +427,7 @@ void ShapeCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast2D::force_shapecast_update);
ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast2D::get_collider);
+ ClassDB::bind_method(D_METHOD("get_collider_rid", "index"), &ShapeCast2D::get_collider_rid);
ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast2D::get_collider_shape);
ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast2D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast2D::get_collision_normal);
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index 7b55566b01..134f236d3b 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -104,6 +104,7 @@ public:
int get_collision_count() const;
Object *get_collider(int p_idx) const;
+ RID get_collider_rid(int p_idx) const;
int get_collider_shape(int p_idx) const;
Vector2 get_collision_point(int p_idx) const;
Vector2 get_collision_normal(int p_idx) const;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 02d8e20ce8..62787d4488 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -213,7 +213,7 @@ void Bone2D::_notification(int p_what) {
}
// Undo scaling
- Transform2D editor_gizmo_trans = Transform2D();
+ Transform2D editor_gizmo_trans;
editor_gizmo_trans.set_scale(Vector2(1, 1) / get_global_scale());
RenderingServer::get_singleton()->canvas_item_set_transform(editor_gizmo_rid, editor_gizmo_trans);
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index f7a66215b2..40e8818262 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -2440,7 +2440,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_
// Find the adequate neighbor
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
+ if (is_existing_neighbor(bit)) {
Vector2i neighbor = get_neighbor_cell(coords, bit);
if (!can_modify_set.has(neighbor)) {
can_modify_list.push_back(neighbor);
@@ -2538,7 +2538,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay
TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX;
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
- if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) {
+ if (is_existing_neighbor(bit)) {
if (get_neighbor_cell(p_path[i], bit) == p_path[i + 1]) {
found_bit = bit;
break;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 7f3e241871..eaf100631e 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -115,7 +115,7 @@ public:
class TerrainConstraint {
private:
const TileMap *tile_map;
- Vector2i base_cell_coords = Vector2i();
+ Vector2i base_cell_coords;
int bit = -1;
int terrain = -1;
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index a98be62dfa..ca23fe03a2 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -330,7 +330,7 @@ bool CollisionObject3D::_are_collision_shapes_visible() {
void CollisionObject3D::_update_shape_data(uint32_t p_owner) {
if (_are_collision_shapes_visible()) {
if (debug_shapes_to_update.is_empty()) {
- callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferredp({}, 0);
+ callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred();
}
debug_shapes_to_update.insert(p_owner);
}
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 7de685b469..5ac8535bb6 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -867,7 +867,7 @@ void CPUParticles3D::_particles_process(double p_delta) {
real_t ring_random_angle = Math::randf() * Math_TAU;
real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;
Vector3 axis = emission_ring_axis.normalized();
- Vector3 ortho_axis = Vector3();
+ Vector3 ortho_axis;
if (axis == Vector3(1.0, 0.0, 0.0)) {
ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis);
} else {
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index dbbf196f7a..17dfe2610e 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -176,22 +176,22 @@ void GPUParticles3D::set_draw_order(DrawOrder p_order) {
void GPUParticles3D::set_trail_enabled(bool p_enabled) {
trail_enabled = p_enabled;
- RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length);
+ RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
update_configuration_warnings();
}
-void GPUParticles3D::set_trail_length(double p_seconds) {
+void GPUParticles3D::set_trail_lifetime(double p_seconds) {
ERR_FAIL_COND(p_seconds < 0.001);
- trail_length = p_seconds;
- RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length);
+ trail_lifetime = p_seconds;
+ RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_lifetime);
}
bool GPUParticles3D::is_trail_enabled() const {
return trail_enabled;
}
-double GPUParticles3D::get_trail_length() const {
- return trail_length;
+double GPUParticles3D::get_trail_lifetime() const {
+ return trail_lifetime;
}
GPUParticles3D::DrawOrder GPUParticles3D::get_draw_order() const {
@@ -272,10 +272,6 @@ bool GPUParticles3D::get_interpolate() const {
PackedStringArray GPUParticles3D::get_configuration_warnings() const {
PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings();
- if (RenderingServer::get_singleton()->is_low_end()) {
- warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."));
- }
-
bool meshes_found = false;
bool anim_material_found = false;
@@ -552,10 +548,10 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle);
ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles3D::set_trail_enabled);
- ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles3D::set_trail_length);
+ ClassDB::bind_method(D_METHOD("set_trail_lifetime", "secs"), &GPUParticles3D::set_trail_lifetime);
ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles3D::is_trail_enabled);
- ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles3D::get_trail_length);
+ ClassDB::bind_method(D_METHOD("get_trail_lifetime"), &GPUParticles3D::get_trail_lifetime);
ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align);
ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align);
@@ -583,7 +579,7 @@ void GPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,Z-Billboard,Y to Velocity,Z-Billboard + Y to Velocity"), "set_transform_align", "get_transform_align");
ADD_GROUP("Trails", "trail_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), "set_trail_length", "get_trail_length");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime");
ADD_GROUP("Process Material", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticleProcessMaterial"), "set_process_material", "get_process_material");
ADD_GROUP("Draw Passes", "draw_");
@@ -627,7 +623,7 @@ GPUParticles3D::GPUParticles3D() {
set_pre_process_time(0);
set_explosiveness_ratio(0);
set_randomness_ratio(0);
- set_trail_length(0.3);
+ set_trail_lifetime(0.3);
set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)));
set_use_local_coordinates(false);
set_draw_passes(1);
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index ef92218e4d..d1768436e5 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -76,13 +76,13 @@ private:
real_t collision_base_size = 0.01;
bool trail_enabled = false;
- double trail_length = 0.3;
+ double trail_lifetime = 0.3;
TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED;
Ref<Material> process_material;
- DrawOrder draw_order;
+ DrawOrder draw_order = DRAW_ORDER_INDEX;
Vector<Ref<Mesh>> draw_passes;
Ref<Skin> skin;
@@ -112,7 +112,7 @@ public:
void set_speed_scale(double p_scale);
void set_collision_base_size(real_t p_ratio);
void set_trail_enabled(bool p_enabled);
- void set_trail_length(double p_seconds);
+ void set_trail_lifetime(double p_seconds);
bool is_emitting() const;
int get_amount() const;
@@ -127,7 +127,7 @@ public:
double get_speed_scale() const;
real_t get_collision_base_size() const;
bool is_trail_enabled() const;
- double get_trail_length() const;
+ double get_trail_lifetime() const;
void set_fixed_fps(int p_count);
int get_fixed_fps() const;
diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h
index 1fc03546fa..46bc6318b8 100644
--- a/scene/3d/navigation_link_3d.h
+++ b/scene/3d/navigation_link_3d.h
@@ -37,11 +37,11 @@ class NavigationLink3D : public Node3D {
GDCLASS(NavigationLink3D, Node3D);
bool enabled = true;
- RID link = RID();
+ RID link;
bool bidirectional = true;
uint32_t navigation_layers = 1;
- Vector3 end_location = Vector3();
- Vector3 start_location = Vector3();
+ Vector3 end_location;
+ Vector3 start_location;
real_t enter_cost = 0.0;
real_t travel_cost = 1.0;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 1743fa1838..1327bdd6e9 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -392,27 +392,25 @@ Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const {
return data.rotation_edit_mode;
}
-void Node3D::set_rotation_order(RotationOrder p_order) {
- Basis::EulerOrder order = Basis::EulerOrder(p_order);
-
- if (data.euler_rotation_order == order) {
+void Node3D::set_rotation_order(EulerOrder p_order) {
+ if (data.euler_rotation_order == p_order) {
return;
}
- ERR_FAIL_INDEX(int32_t(order), 6);
+ ERR_FAIL_INDEX(int32_t(p_order), 6);
bool transform_changed = false;
if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
_update_rotation_and_scale();
} else if (data.dirty & DIRTY_LOCAL_TRANSFORM) {
- data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(order);
+ data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(p_order);
transform_changed = true;
} else {
data.dirty |= DIRTY_LOCAL_TRANSFORM;
transform_changed = true;
}
- data.euler_rotation_order = order;
+ data.euler_rotation_order = p_order;
if (transform_changed) {
_propagate_transform_changed(this);
@@ -423,8 +421,8 @@ void Node3D::set_rotation_order(RotationOrder p_order) {
notify_property_list_changed(); // Will change the rotation property.
}
-Node3D::RotationOrder Node3D::get_rotation_order() const {
- return RotationOrder(data.euler_rotation_order);
+EulerOrder Node3D::get_rotation_order() const {
+ return data.euler_rotation_order;
}
void Node3D::set_rotation(const Vector3 &p_euler_rad) {
@@ -1042,13 +1040,6 @@ void Node3D::_bind_methods() {
BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION);
BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_BASIS);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_XYZ);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_XZY);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_YXZ);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_YZX);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY);
- BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX);
-
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NO_EDITOR), "set_transform", "get_transform");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "suffix:m", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 457f634c34..76c1e3e22c 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -61,15 +61,6 @@ public:
ROTATION_EDIT_MODE_BASIS,
};
- enum RotationOrder {
- ROTATION_ORDER_XYZ,
- ROTATION_ORDER_XZY,
- ROTATION_ORDER_YXZ,
- ROTATION_ORDER_YZX,
- ROTATION_ORDER_ZXY,
- ROTATION_ORDER_ZYX
- };
-
private:
// For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaternion/Scale and Euler Rotation/Scale.
// Transform and Quaternion are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand).
@@ -100,7 +91,7 @@ private:
struct Data {
mutable Transform3D global_transform;
mutable Transform3D local_transform;
- mutable Basis::EulerOrder euler_rotation_order = Basis::EULER_ORDER_YXZ;
+ mutable EulerOrder euler_rotation_order = EulerOrder::YXZ;
mutable Vector3 euler_rotation;
mutable Vector3 scale = Vector3(1, 1, 1);
mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
@@ -179,7 +170,7 @@ public:
void set_rotation_edit_mode(RotationEditMode p_mode);
RotationEditMode get_rotation_edit_mode() const;
- void set_rotation_order(RotationOrder p_order);
+ void set_rotation_order(EulerOrder p_order);
void set_rotation(const Vector3 &p_euler_rad);
void set_scale(const Vector3 &p_scale);
@@ -188,7 +179,7 @@ public:
Vector3 get_position() const;
- RotationOrder get_rotation_order() const;
+ EulerOrder get_rotation_order() const;
Vector3 get_rotation() const;
Vector3 get_scale() const;
@@ -277,6 +268,5 @@ public:
};
VARIANT_ENUM_CAST(Node3D::RotationEditMode)
-VARIANT_ENUM_CAST(Node3D::RotationOrder)
#endif // NODE_3D_H
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index ff05e88241..817ffbfec8 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -68,7 +68,7 @@ void RemoteTransform3D::_update_remote() {
Transform3D our_trans = get_global_transform();
if (update_remote_rotation) {
- n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
+ n->set_rotation(our_trans.basis.get_euler_normalized(EulerOrder(n->get_rotation_order())));
}
if (update_remote_scale) {
@@ -90,7 +90,7 @@ void RemoteTransform3D::_update_remote() {
Transform3D our_trans = get_transform();
if (update_remote_rotation) {
- n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
+ n->set_rotation(our_trans.basis.get_euler_normalized(EulerOrder(n->get_rotation_order())));
}
if (update_remote_scale) {
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index 03cbd984cd..267139c7d0 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -115,6 +115,7 @@ void ShapeCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("force_shapecast_update"), &ShapeCast3D::force_shapecast_update);
ClassDB::bind_method(D_METHOD("get_collider", "index"), &ShapeCast3D::get_collider);
+ ClassDB::bind_method(D_METHOD("get_collider_rid", "index"), &ShapeCast3D::get_collider_rid);
ClassDB::bind_method(D_METHOD("get_collider_shape", "index"), &ShapeCast3D::get_collider_shape);
ClassDB::bind_method(D_METHOD("get_collision_point", "index"), &ShapeCast3D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal", "index"), &ShapeCast3D::get_collision_normal);
@@ -282,6 +283,11 @@ Object *ShapeCast3D::get_collider(int p_idx) const {
return ObjectDB::get_instance(result[p_idx].collider_id);
}
+RID ShapeCast3D::get_collider_rid(int p_idx) const {
+ ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), RID(), "No collider RID found.");
+ return result[p_idx].rid;
+}
+
int ShapeCast3D::get_collider_shape(int p_idx) const {
ERR_FAIL_INDEX_V_MSG(p_idx, result.size(), -1, "No collider shape found.");
return result[p_idx].shape;
diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h
index 2526d8d32c..abe10c9b24 100644
--- a/scene/3d/shape_cast_3d.h
+++ b/scene/3d/shape_cast_3d.h
@@ -120,6 +120,7 @@ public:
int get_collision_count() const;
Object *get_collider(int p_idx) const;
+ RID get_collider_rid(int p_idx) const;
int get_collider_shape(int p_idx) const;
Vector3 get_collision_point(int p_idx) const;
Vector3 get_collision_normal(int p_idx) const;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index be6eab2178..d69953fee5 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -680,6 +680,7 @@ void Sprite3D::set_region_enabled(bool p_region) {
region = p_region;
_queue_redraw();
+ notify_property_list_changed();
}
bool Sprite3D::is_region_enabled() const {
@@ -781,6 +782,10 @@ void Sprite3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "frame_coords") {
p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ if (!region && (p_property.name == "region_rect")) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
}
void Sprite3D::_bind_methods() {
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 8f5a51eff6..b5b37bf837 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -74,10 +74,6 @@ RID VisualInstance3D::get_instance() const {
return instance;
}
-RID VisualInstance3D::_get_visual_instance_rid() const {
- return instance;
-}
-
void VisualInstance3D::set_layer_mask(uint32_t p_mask) {
layers = p_mask;
RenderingServer::get_singleton()->instance_set_layer_mask(instance, p_mask);
@@ -106,7 +102,6 @@ bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const {
}
void VisualInstance3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance3D::_get_visual_instance_rid);
ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance3D::set_base);
ClassDB::bind_method(D_METHOD("get_base"), &VisualInstance3D::get_base);
ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance3D::get_instance);
@@ -156,7 +151,7 @@ Ref<Material> GeometryInstance3D::get_material_overlay() const {
return material_overlay;
}
-void GeometryInstance3D::set_transparecy(float p_transparency) {
+void GeometryInstance3D::set_transparency(float p_transparency) {
transparency = CLAMP(p_transparency, 0.0f, 1.0f);
RS::get_singleton()->instance_geometry_set_transparency(get_instance(), transparency);
}
@@ -216,15 +211,20 @@ GeometryInstance3D::VisibilityRangeFadeMode GeometryInstance3D::get_visibility_r
}
const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName p_name) const {
- StringName *r = instance_uniform_property_remap.getptr(p_name);
+ StringName *r = instance_shader_parameter_property_remap.getptr(p_name);
if (!r) {
String s = p_name;
+#ifndef DISABLE_DEPRECATED
if (s.begins_with("shader_uniforms/")) {
- StringName name = s.replace("shader_uniforms/", "");
- instance_uniform_property_remap[p_name] = name;
- return instance_uniform_property_remap.getptr(p_name);
+ s = s.replace("shader_uniforms/", "instance_shader_parameters/");
+ }
+#endif // DISABLE_DEPRECATED
+ if (s.begins_with("instance_shader_parameters/")) {
+ StringName pname = StringName(s);
+ StringName name = s.replace("instance_shader_parameters/", "");
+ instance_shader_parameter_property_remap[pname] = name;
+ return instance_shader_parameter_property_remap.getptr(pname);
}
-
return nullptr;
}
@@ -247,7 +247,7 @@ bool GeometryInstance3D::_set(const StringName &p_name, const Variant &p_value)
set_gi_mode(GI_MODE_DYNAMIC);
return true;
}
-#endif
+#endif // DISABLE_DEPRECATED
return false;
}
@@ -270,13 +270,13 @@ void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const {
if (def_value.get_type() != Variant::NIL) {
has_def_value = true;
}
- if (instance_uniforms.has(pi.name)) {
+ if (instance_shader_parameters.has(pi.name)) {
pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE);
} else {
pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); //do not save if not changed
}
- pi.name = "shader_uniforms/" + pi.name;
+ pi.name = "instance_shader_parameters/" + pi.name;
p_list->push_back(pi);
}
}
@@ -315,9 +315,9 @@ void GeometryInstance3D::set_instance_shader_parameter(const StringName &p_name,
if (p_value.get_type() == Variant::NIL) {
Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_name);
RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, def_value);
- instance_uniforms.erase(p_value);
+ instance_shader_parameters.erase(p_value);
} else {
- instance_uniforms[p_name] = p_value;
+ instance_shader_parameters[p_name] = p_value;
if (p_value.get_type() == Variant::OBJECT) {
RID tex_id = p_value;
RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_name, tex_id);
@@ -408,7 +408,7 @@ void GeometryInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias);
ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias);
- ClassDB::bind_method(D_METHOD("set_transparency", "transparency"), &GeometryInstance3D::set_transparecy);
+ ClassDB::bind_method(D_METHOD("set_transparency", "transparency"), &GeometryInstance3D::set_transparency);
ClassDB::bind_method(D_METHOD("get_transparency"), &GeometryInstance3D::get_transparency);
ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin);
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index 06a8c05faa..f18bff2ddc 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -40,8 +40,6 @@ class VisualInstance3D : public Node3D {
RID instance;
uint32_t layers = 1;
- RID _get_visual_instance_rid() const;
-
protected:
void _update_visibility();
@@ -119,8 +117,8 @@ private:
float lod_bias = 1.0;
- mutable HashMap<StringName, Variant> instance_uniforms;
- mutable HashMap<StringName, StringName> instance_uniform_property_remap;
+ mutable HashMap<StringName, Variant> instance_shader_parameters;
+ mutable HashMap<StringName, StringName> instance_shader_parameter_property_remap;
float extra_cull_margin = 0.0;
LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X;
@@ -140,7 +138,7 @@ public:
void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting);
ShadowCastingSetting get_cast_shadows_setting() const;
- void set_transparecy(float p_transparency);
+ void set_transparency(float p_transparency);
float get_transparency() const;
void set_visibility_range_begin(float p_dist);
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index f5d30c584f..ca7d1dfc1d 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -644,6 +644,12 @@ void XROrigin3D::set_current(bool p_enabled) {
origin_nodes[i]->set_current(false);
}
}
+
+ // update XRServer with our current position
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ xr_server->set_world_origin(get_global_transform());
} else {
bool found = false;
// We no longer have a current origin so find the first one we can make current
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index b4bdda9ecb..df27eecce7 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -39,87 +39,10 @@
#include "scene/main/window.h"
#include "scene/resources/packed_scene.h"
-Array SceneDebugger::RPCProfilerFrame::serialize() {
- Array arr;
- arr.push_back(infos.size() * 4);
- for (int i = 0; i < infos.size(); ++i) {
- arr.push_back(uint64_t(infos[i].node));
- arr.push_back(infos[i].node_path);
- arr.push_back(infos[i].incoming_rpc);
- arr.push_back(infos[i].outgoing_rpc);
- }
- return arr;
-}
-
-bool SceneDebugger::RPCProfilerFrame::deserialize(const Array &p_arr) {
- ERR_FAIL_COND_V(p_arr.size() < 1, false);
- uint32_t size = p_arr[0];
- ERR_FAIL_COND_V(size % 4, false);
- ERR_FAIL_COND_V((uint32_t)p_arr.size() != size + 1, false);
- infos.resize(size / 4);
- int idx = 1;
- for (uint32_t i = 0; i < size / 4; ++i) {
- infos.write[i].node = uint64_t(p_arr[idx]);
- infos.write[i].node_path = p_arr[idx + 1];
- infos.write[i].incoming_rpc = p_arr[idx + 2];
- infos.write[i].outgoing_rpc = p_arr[idx + 3];
- }
- return true;
-}
-
-class SceneDebugger::RPCProfiler : public EngineProfiler {
- HashMap<ObjectID, RPCNodeInfo> rpc_node_data;
- uint64_t last_profile_time = 0;
-
- void init_node(const ObjectID p_node) {
- if (rpc_node_data.has(p_node)) {
- return;
- }
- rpc_node_data.insert(p_node, RPCNodeInfo());
- rpc_node_data[p_node].node = p_node;
- rpc_node_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path();
- rpc_node_data[p_node].incoming_rpc = 0;
- rpc_node_data[p_node].outgoing_rpc = 0;
- }
-
-public:
- void toggle(bool p_enable, const Array &p_opts) {
- rpc_node_data.clear();
- }
-
- void add(const Array &p_data) {
- ERR_FAIL_COND(p_data.size() < 2);
- const ObjectID id = p_data[0];
- const String what = p_data[1];
- init_node(id);
- RPCNodeInfo &info = rpc_node_data[id];
- if (what == "rpc_in") {
- info.incoming_rpc++;
- } else if (what == "rpc_out") {
- info.outgoing_rpc++;
- }
- }
-
- void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) {
- uint64_t pt = OS::get_singleton()->get_ticks_msec();
- if (pt - last_profile_time > 100) {
- last_profile_time = pt;
- RPCProfilerFrame frame;
- for (const KeyValue<ObjectID, RPCNodeInfo> &E : rpc_node_data) {
- frame.infos.push_back(E.value);
- }
- rpc_node_data.clear();
- EngineDebugger::get_singleton()->send_message("multiplayer:rpc", frame.serialize());
- }
- }
-};
-
SceneDebugger *SceneDebugger::singleton = nullptr;
SceneDebugger::SceneDebugger() {
singleton = this;
- rpc_profiler.instantiate();
- rpc_profiler->bind("rpc");
#ifdef DEBUG_ENABLED
LiveEditor::singleton = memnew(LiveEditor);
EngineDebugger::register_message_capture("scene", EngineDebugger::Capture(nullptr, SceneDebugger::parse_message));
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index fe35446aae..0428bfcc2e 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -42,28 +42,9 @@ class Node;
class SceneDebugger {
public:
- // RPC profiler
- struct RPCNodeInfo {
- ObjectID node;
- String node_path;
- int incoming_rpc = 0;
- int outgoing_rpc = 0;
- };
-
- struct RPCProfilerFrame {
- Vector<RPCNodeInfo> infos;
-
- Array serialize();
- bool deserialize(const Array &p_arr);
- };
-
private:
- class RPCProfiler;
-
static SceneDebugger *singleton;
- Ref<RPCProfiler> rpc_profiler;
-
SceneDebugger();
public:
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 552345e4fe..ac9034c6fd 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -127,6 +127,7 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
status.press_attempt = false;
status.pressing_inside = false;
+ status.shortcut_press = false;
} break;
}
}
@@ -160,6 +161,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
if (action_mode == ACTION_MODE_BUTTON_PRESS) {
status.press_attempt = false;
status.pressing_inside = false;
+ status.shortcut_press = false;
}
status.pressed = !status.pressed;
_unpress_group();
@@ -185,6 +187,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
}
status.press_attempt = false;
status.pressing_inside = false;
+ status.shortcut_press = false;
emit_signal(SNAME("button_up"));
}
@@ -209,6 +212,7 @@ void BaseButton::set_disabled(bool p_disabled) {
}
status.press_attempt = false;
status.pressing_inside = false;
+ status.shortcut_press = false;
}
queue_redraw();
}
@@ -218,13 +222,12 @@ bool BaseButton::is_disabled() const {
}
void BaseButton::set_pressed(bool p_pressed) {
- if (!toggle_mode) {
- return;
- }
- if (status.pressed == p_pressed) {
+ bool prev_pressed = status.pressed;
+ set_pressed_no_signal(p_pressed);
+
+ if (status.pressed == prev_pressed) {
return;
}
- status.pressed = p_pressed;
if (p_pressed) {
_unpress_group();
@@ -233,8 +236,6 @@ void BaseButton::set_pressed(bool p_pressed) {
}
}
_toggled(status.pressed);
-
- queue_redraw();
}
void BaseButton::set_pressed_no_signal(bool p_pressed) {
@@ -284,7 +285,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
pressing = status.pressed;
}
- if (pressing) {
+ if ((shortcut_feedback || !status.shortcut_press) && pressing) {
return DRAW_PRESSED;
} else {
return DRAW_NORMAL;
@@ -350,6 +351,7 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) {
+ status.shortcut_press = true;
on_action_event(p_event);
accept_event();
}
@@ -389,6 +391,14 @@ bool BaseButton::_was_pressed_by_mouse() const {
return was_mouse_pressed;
}
+void BaseButton::set_shortcut_feedback(bool p_feedback) {
+ shortcut_feedback = p_feedback;
+}
+
+bool BaseButton::is_shortcut_feedback() const {
+ return shortcut_feedback;
+}
+
void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed);
ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed);
@@ -414,6 +424,9 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_button_group", "button_group"), &BaseButton::set_button_group);
ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group);
+ ClassDB::bind_method(D_METHOD("set_shortcut_feedback", "enabled"), &BaseButton::set_shortcut_feedback);
+ ClassDB::bind_method(D_METHOD("is_shortcut_feedback"), &BaseButton::is_shortcut_feedback);
+
GDVIRTUAL_BIND(_pressed);
GDVIRTUAL_BIND(_toggled, "button_pressed");
@@ -430,6 +443,7 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_feedback"), "set_shortcut_feedback", "is_shortcut_feedback");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
BIND_ENUM_CONSTANT(DRAW_NORMAL);
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 7839239800..3acf535f54 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -53,6 +53,7 @@ private:
bool keep_pressed_outside = false;
Ref<Shortcut> shortcut;
ObjectID shortcut_context;
+ bool shortcut_feedback = true;
ActionMode action_mode = ACTION_MODE_BUTTON_RELEASE;
struct Status {
@@ -60,6 +61,7 @@ private:
bool hovering = false;
bool press_attempt = false;
bool pressing_inside = false;
+ bool shortcut_press = false;
bool disabled = false;
@@ -131,6 +133,9 @@ public:
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
+ void set_shortcut_feedback(bool p_feedback);
+ bool is_shortcut_feedback() const;
+
BaseButton();
~BaseButton();
};
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index c2b82e01d1..0e7bc5c306 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -231,7 +231,7 @@ void Button::_notification(int p_what) {
_icon = icon;
}
- Rect2 icon_region = Rect2();
+ Rect2 icon_region;
HorizontalAlignment icon_align_rtl_checked = icon_alignment;
HorizontalAlignment align_rtl_checked = alignment;
// Swap icon and text alignment sides if right-to-left layout is set.
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index f61fa29a33..ea310f5a12 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -138,7 +138,7 @@ void CodeEdit::_notification(int p_what) {
code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0);
code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height);
- code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines);
+ code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color);
for (int i = 0; i < lines; i++) {
@@ -281,16 +281,22 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
case MouseButton::WHEEL_UP: {
if (code_completion_current_selected > 0) {
code_completion_current_selected--;
+ code_completion_force_item_center = -1;
queue_redraw();
}
} break;
case MouseButton::WHEEL_DOWN: {
if (code_completion_current_selected < code_completion_options.size() - 1) {
code_completion_current_selected++;
+ code_completion_force_item_center = -1;
queue_redraw();
}
} break;
case MouseButton::LEFT: {
+ if (code_completion_force_item_center == -1) {
+ code_completion_force_item_center = code_completion_current_selected;
+ }
+
code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1);
if (mb->is_double_click()) {
confirm_code_completion();
@@ -300,6 +306,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
default:
break;
}
+
return;
} else if (code_completion_active && code_completion_scroll_rect.has_point(mb->get_position())) {
if (mb->get_button_index() != MouseButton::LEFT) {
@@ -448,6 +455,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
code_completion_current_selected = code_completion_options.size() - 1;
}
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
@@ -458,30 +466,35 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
code_completion_current_selected = 0;
}
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
}
if (k->is_action("ui_page_up", true)) {
code_completion_current_selected = MAX(0, code_completion_current_selected - code_completion_max_lines);
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
}
if (k->is_action("ui_page_down", true)) {
code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines);
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
}
if (k->is_action("ui_home", true)) {
code_completion_current_selected = 0;
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
}
if (k->is_action("ui_end", true)) {
code_completion_current_selected = code_completion_options.size() - 1;
+ code_completion_force_item_center = -1;
queue_redraw();
accept_event();
return;
@@ -681,8 +694,8 @@ void CodeEdit::_backspace_internal(int p_caret) {
return;
}
- if (has_selection()) {
- delete_selection();
+ if (has_selection(p_caret)) {
+ delete_selection(p_caret);
return;
}
@@ -1978,6 +1991,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) {
}
ERR_FAIL_INDEX(p_index, code_completion_options.size());
code_completion_current_selected = p_index;
+ code_completion_force_item_center = -1;
queue_redraw();
}
@@ -2808,6 +2822,7 @@ void CodeEdit::_update_scroll_selected_line(float p_mouse_y) {
percent = CLAMP(percent, 0.0f, 1.0f);
code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1));
+ code_completion_force_item_center = -1;
}
void CodeEdit::_filter_code_completion_candidates_impl() {
@@ -2867,6 +2882,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size);
code_completion_current_selected = 0;
+ code_completion_force_item_center = -1;
code_completion_active = true;
queue_redraw();
return;
@@ -3123,6 +3139,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size);
code_completion_current_selected = 0;
+ code_completion_force_item_center = -1;
code_completion_active = true;
queue_redraw();
}
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 09c7ef80bf..cbbc13480e 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -214,6 +214,7 @@ private:
Vector<ScriptLanguage::CodeCompletionOption> code_completion_options;
int code_completion_line_ofs = 0;
int code_completion_current_selected = 0;
+ int code_completion_force_item_center = -1;
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
Rect2i code_completion_scroll_rect;
diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp
index a063cd344a..308fe057c5 100644
--- a/scene/gui/color_mode.cpp
+++ b/scene/gui/color_mode.cpp
@@ -158,8 +158,7 @@ void ColorModeHSV::slider_draw(int p_which) {
right_color.a = 1;
} else if (p_which == 0) {
Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
- slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
- slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false);
+ slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false);
return;
} else {
Color s_col;
@@ -289,9 +288,8 @@ void ColorModeOKHSL::slider_draw(int p_which) {
const real_t margin = 16 * color_picker->get_theme_default_base_scale();
if (p_which == 0) { // H
- Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
- slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
- slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false);
+ Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_okhsl_hue"), SNAME("ColorPicker"));
+ slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false);
return;
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 1b87c1d709..724e5bcaf6 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -253,18 +253,20 @@ void ColorPicker::_update_controls() {
wheel_edit->hide();
w_edit->show();
uv_edit->show();
+ btn_shape->show();
break;
case SHAPE_HSV_WHEEL:
wheel_edit->show();
w_edit->hide();
uv_edit->hide();
-
+ btn_shape->show();
wheel->set_material(wheel_mat);
break;
case SHAPE_VHS_CIRCLE:
wheel_edit->show();
w_edit->show();
uv_edit->hide();
+ btn_shape->show();
wheel->set_material(circle_mat);
circle_mat->set_shader(circle_shader);
break;
@@ -272,9 +274,16 @@ void ColorPicker::_update_controls() {
wheel_edit->show();
w_edit->show();
uv_edit->hide();
+ btn_shape->show();
wheel->set_material(circle_mat);
circle_mat->set_shader(circle_ok_color_shader);
break;
+ case SHAPE_NONE:
+ wheel_edit->hide();
+ w_edit->hide();
+ uv_edit->hide();
+ btn_shape->hide();
+ break;
default: {
}
}
@@ -606,10 +615,13 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
if (p_shape == current_shape) {
return;
}
- shape_popup->set_item_checked(current_shape, false);
- shape_popup->set_item_checked(p_shape, true);
-
- btn_shape->set_icon(shape_popup->get_item_icon(p_shape));
+ if (current_shape != SHAPE_NONE) {
+ shape_popup->set_item_checked(current_shape, false);
+ }
+ if (p_shape != SHAPE_NONE) {
+ shape_popup->set_item_checked(p_shape, true);
+ btn_shape->set_icon(shape_popup->get_item_icon(p_shape));
+ }
current_shape = p_shape;
@@ -1331,7 +1343,7 @@ void ColorPicker::_preset_input(const Ref<InputEvent> &p_event, const Color &p_c
set_pick_color(p_color);
add_recent_preset(color);
emit_signal(SNAME("color_changed"), p_color);
- } else if (bev->is_pressed() && bev->get_button_index() == MouseButton::RIGHT && presets_enabled) {
+ } else if (bev->is_pressed() && bev->get_button_index() == MouseButton::RIGHT && can_add_swatches) {
erase_preset(p_color);
emit_signal(SNAME("preset_removed"), p_color);
}
@@ -1421,11 +1433,11 @@ void ColorPicker::_html_focus_exit() {
_html_submitted(c_text->get_text());
}
-void ColorPicker::set_presets_enabled(bool p_enabled) {
- if (presets_enabled == p_enabled) {
+void ColorPicker::set_can_add_swatches(bool p_enabled) {
+ if (can_add_swatches == p_enabled) {
return;
}
- presets_enabled = p_enabled;
+ can_add_swatches = p_enabled;
if (!p_enabled) {
btn_add_preset->set_disabled(true);
btn_add_preset->set_focus_mode(FOCUS_NONE);
@@ -1435,8 +1447,8 @@ void ColorPicker::set_presets_enabled(bool p_enabled) {
}
}
-bool ColorPicker::are_presets_enabled() const {
- return presets_enabled;
+bool ColorPicker::are_swatches_enabled() const {
+ return can_add_swatches;
}
void ColorPicker::set_presets_visible(bool p_visible) {
@@ -1444,13 +1456,62 @@ void ColorPicker::set_presets_visible(bool p_visible) {
return;
}
presets_visible = p_visible;
- preset_container->set_visible(p_visible);
+ btn_preset->set_visible(p_visible);
+ btn_recent_preset->set_visible(p_visible);
}
bool ColorPicker::are_presets_visible() const {
return presets_visible;
}
+void ColorPicker::set_modes_visible(bool p_visible) {
+ if (color_modes_visible == p_visible) {
+ return;
+ }
+ color_modes_visible = p_visible;
+ mode_hbc->set_visible(p_visible);
+}
+
+bool ColorPicker::are_modes_visible() const {
+ return color_modes_visible;
+}
+
+void ColorPicker::set_sampler_visible(bool p_visible) {
+ if (sampler_visible == p_visible) {
+ return;
+ }
+ sampler_visible = p_visible;
+ sample_hbc->set_visible(p_visible);
+}
+
+bool ColorPicker::is_sampler_visible() const {
+ return sampler_visible;
+}
+
+void ColorPicker::set_sliders_visible(bool p_visible) {
+ if (sliders_visible == p_visible) {
+ return;
+ }
+ sliders_visible = p_visible;
+ slider_gc->set_visible(p_visible);
+}
+
+bool ColorPicker::are_sliders_visible() const {
+ return sliders_visible;
+}
+
+void ColorPicker::set_hex_visible(bool p_visible) {
+ if (hex_visible == p_visible) {
+ return;
+ }
+ hex_visible = p_visible;
+ hex_hbc->set_visible(p_visible);
+}
+
+bool ColorPicker::is_hex_visible() const {
+ return hex_visible;
+}
+
void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
@@ -1460,10 +1521,18 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_color_mode"), &ColorPicker::get_color_mode);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
- ClassDB::bind_method(D_METHOD("set_presets_enabled", "enabled"), &ColorPicker::set_presets_enabled);
- ClassDB::bind_method(D_METHOD("are_presets_enabled"), &ColorPicker::are_presets_enabled);
+ ClassDB::bind_method(D_METHOD("set_can_add_swatches", "enabled"), &ColorPicker::set_can_add_swatches);
+ ClassDB::bind_method(D_METHOD("are_swatches_enabled"), &ColorPicker::are_swatches_enabled);
ClassDB::bind_method(D_METHOD("set_presets_visible", "visible"), &ColorPicker::set_presets_visible);
ClassDB::bind_method(D_METHOD("are_presets_visible"), &ColorPicker::are_presets_visible);
+ ClassDB::bind_method(D_METHOD("set_modes_visible", "visible"), &ColorPicker::set_modes_visible);
+ ClassDB::bind_method(D_METHOD("are_modes_visible"), &ColorPicker::are_modes_visible);
+ ClassDB::bind_method(D_METHOD("set_sampler_visible", "visible"), &ColorPicker::set_sampler_visible);
+ ClassDB::bind_method(D_METHOD("is_sampler_visible"), &ColorPicker::is_sampler_visible);
+ ClassDB::bind_method(D_METHOD("set_sliders_visible", "visible"), &ColorPicker::set_sliders_visible);
+ ClassDB::bind_method(D_METHOD("are_sliders_visible"), &ColorPicker::are_sliders_visible);
+ ClassDB::bind_method(D_METHOD("set_hex_visible", "visible"), &ColorPicker::set_hex_visible);
+ ClassDB::bind_method(D_METHOD("is_hex_visible"), &ColorPicker::is_hex_visible);
ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset);
ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets);
@@ -1481,8 +1550,13 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL"), "set_color_mode", "get_color_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle"), "set_picker_shape", "get_picker_shape");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle,None"), "set_picker_shape", "get_picker_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_add_swatches"), "set_can_add_swatches", "are_swatches_enabled");
+ ADD_GROUP("Customization", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sampler_visible"), "set_sampler_visible", "is_sampler_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "color_modes_visible"), "set_modes_visible", "are_modes_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sliders_visible"), "set_sliders_visible", "are_sliders_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hex_visible"), "set_hex_visible", "is_hex_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_visible"), "set_presets_visible", "are_presets_visible");
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
@@ -1498,6 +1572,7 @@ void ColorPicker::_bind_methods() {
BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL);
BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
BIND_ENUM_CONSTANT(SHAPE_OKHSL_CIRCLE);
+ BIND_ENUM_CONSTANT(SHAPE_NONE);
}
ColorPicker::ColorPicker() :
@@ -1514,24 +1589,24 @@ ColorPicker::ColorPicker() :
uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit));
- HBoxContainer *hb_smpl = memnew(HBoxContainer);
- add_child(hb_smpl, false, INTERNAL_MODE_FRONT);
+ sample_hbc = memnew(HBoxContainer);
+ add_child(sample_hbc, false, INTERNAL_MODE_FRONT);
btn_pick = memnew(Button);
- hb_smpl->add_child(btn_pick);
+ sample_hbc->add_child(btn_pick);
btn_pick->set_toggle_mode(true);
btn_pick->set_tooltip_text(RTR("Pick a color from the editor window."));
btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed));
sample = memnew(TextureRect);
- hb_smpl->add_child(sample);
+ sample_hbc->add_child(sample);
sample->set_h_size_flags(SIZE_EXPAND_FILL);
sample->connect("gui_input", callable_mp(this, &ColorPicker::_sample_input));
sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw));
btn_shape = memnew(MenuButton);
btn_shape->set_flat(false);
- hb_smpl->add_child(btn_shape);
+ sample_hbc->add_child(btn_shape);
btn_shape->set_toggle_mode(true);
btn_shape->set_tooltip_text(RTR("Select a picker shape."));
@@ -1552,7 +1627,7 @@ ColorPicker::ColorPicker() :
add_mode(new ColorModeRAW(this));
add_mode(new ColorModeOKHSL(this));
- HBoxContainer *mode_hbc = memnew(HBoxContainer);
+ mode_hbc = memnew(HBoxContainer);
add_child(mode_hbc, false, INTERNAL_MODE_FRONT);
mode_group.instantiate();
@@ -1598,26 +1673,26 @@ ColorPicker::ColorPicker() :
add_child(vbr, false, INTERNAL_MODE_FRONT);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
- GridContainer *gc = memnew(GridContainer);
+ slider_gc = memnew(GridContainer);
- vbr->add_child(gc);
- gc->set_h_size_flags(SIZE_EXPAND_FILL);
- gc->set_columns(3);
+ vbr->add_child(slider_gc);
+ slider_gc->set_h_size_flags(SIZE_EXPAND_FILL);
+ slider_gc->set_columns(3);
for (int i = 0; i < SLIDER_COUNT + 1; i++) {
- create_slider(gc, i);
+ create_slider(slider_gc, i);
}
alpha_label->set_text("A");
- HBoxContainer *hhb = memnew(HBoxContainer);
- hhb->set_alignment(ALIGNMENT_BEGIN);
- vbr->add_child(hhb);
+ hex_hbc = memnew(HBoxContainer);
+ hex_hbc->set_alignment(ALIGNMENT_BEGIN);
+ vbr->add_child(hex_hbc);
- hhb->add_child(memnew(Label("Hex")));
+ hex_hbc->add_child(memnew(Label("Hex")));
text_type = memnew(Button);
- hhb->add_child(text_type);
+ hex_hbc->add_child(text_type);
text_type->set_text("#");
text_type->set_tooltip_text(RTR("Switch between hexadecimal and code values."));
if (Engine::get_singleton()->is_editor_hint()) {
@@ -1628,7 +1703,7 @@ ColorPicker::ColorPicker() :
}
c_text = memnew(LineEdit);
- hhb->add_child(c_text);
+ hex_hbc->add_child(c_text);
c_text->set_select_all_on_focus(true);
c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted));
c_text->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index a0843d6fa2..3208676539 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -86,6 +86,7 @@ public:
SHAPE_HSV_WHEEL,
SHAPE_VHS_CIRCLE,
SHAPE_OKHSL_CIRCLE,
+ SHAPE_NONE,
SHAPE_MAX
};
@@ -125,6 +126,10 @@ private:
PopupMenu *shape_popup = nullptr;
PopupMenu *mode_popup = nullptr;
MenuButton *btn_shape = nullptr;
+ HBoxContainer *mode_hbc = nullptr;
+ HBoxContainer *sample_hbc = nullptr;
+ GridContainer *slider_gc = nullptr;
+ HBoxContainer *hex_hbc = nullptr;
MenuButton *btn_mode = nullptr;
Button *mode_btns[MODE_BUTTON_COUNT];
Ref<ButtonGroup> mode_group = nullptr;
@@ -165,8 +170,12 @@ private:
bool updating = true;
bool changing_color = false;
bool spinning = false;
- bool presets_enabled = true;
+ bool can_add_swatches = true;
bool presets_visible = true;
+ bool color_modes_visible = true;
+ bool sampler_visible = true;
+ bool sliders_visible = true;
+ bool hex_visible = true;
bool line_edit_mouse_release = false;
bool text_changed = false;
@@ -267,12 +276,24 @@ public:
void set_deferred_mode(bool p_enabled);
bool is_deferred_mode() const;
- void set_presets_enabled(bool p_enabled);
- bool are_presets_enabled() const;
+ void set_can_add_swatches(bool p_enabled);
+ bool are_swatches_enabled() const;
void set_presets_visible(bool p_visible);
bool are_presets_visible() const;
+ void set_modes_visible(bool p_visible);
+ bool are_modes_visible() const;
+
+ void set_sampler_visible(bool p_visible);
+ bool is_sampler_visible() const;
+
+ void set_sliders_visible(bool p_visible);
+ bool are_sliders_visible() const;
+
+ void set_hex_visible(bool p_visible);
+ bool is_hex_visible() const;
+
void set_focus_on_line_edit();
ColorPicker();
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
index b0d15aa7f4..44c5ec62f8 100644
--- a/scene/gui/flow_container.cpp
+++ b/scene/gui/flow_container.cpp
@@ -152,6 +152,28 @@ void FlowContainer::_resort() {
line_data = lines_data[current_line_idx];
}
+ // The first child of each line adds the offset caused by the alignment,
+ // but only if the line doesn't contain a child that expands.
+ if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) {
+ int alignment_ofs = 0;
+ switch (alignment) {
+ case ALIGNMENT_CENTER:
+ alignment_ofs = line_data.stretch_avail / 2;
+ break;
+ case ALIGNMENT_END:
+ alignment_ofs = line_data.stretch_avail;
+ break;
+ default:
+ break;
+ }
+
+ if (vertical) { /* VERTICAL */
+ ofs.y += alignment_ofs;
+ } else { /* HORIZONTAL */
+ ofs.x += alignment_ofs;
+ }
+ }
+
if (vertical) { /* VERTICAL */
if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
child_size.width = line_data.min_line_height;
@@ -282,6 +304,18 @@ int FlowContainer::get_line_count() const {
return cached_line_count;
}
+void FlowContainer::set_alignment(AlignmentMode p_alignment) {
+ if (alignment == p_alignment) {
+ return;
+ }
+ alignment = p_alignment;
+ _resort();
+}
+
+FlowContainer::AlignmentMode FlowContainer::get_alignment() const {
+ return alignment;
+}
+
void FlowContainer::set_vertical(bool p_vertical) {
ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + ".");
vertical = p_vertical;
@@ -300,8 +334,15 @@ FlowContainer::FlowContainer(bool p_vertical) {
void FlowContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+ ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment);
+ ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment);
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical);
ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical);
+ BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_END);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
}
diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h
index 536df27ad6..6a61e9b904 100644
--- a/scene/gui/flow_container.h
+++ b/scene/gui/flow_container.h
@@ -36,11 +36,19 @@
class FlowContainer : public Container {
GDCLASS(FlowContainer, Container);
+public:
+ enum AlignmentMode {
+ ALIGNMENT_BEGIN,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_END
+ };
+
private:
int cached_size = 0;
int cached_line_count = 0;
bool vertical = false;
+ AlignmentMode alignment = ALIGNMENT_BEGIN;
struct ThemeCache {
int h_separation = 0;
@@ -61,6 +69,9 @@ protected:
public:
int get_line_count() const;
+ void set_alignment(AlignmentMode p_alignment);
+ AlignmentMode get_alignment() const;
+
void set_vertical(bool p_vertical);
bool is_vertical() const;
@@ -88,4 +99,6 @@ public:
FlowContainer(true) { is_fixed = true; }
};
+VARIANT_ENUM_CAST(FlowContainer::AlignmentMode);
+
#endif // FLOW_CONTAINER_H
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index d6b5557a3f..82f089735d 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -896,7 +896,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (k.is_valid() && k->get_unicode()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
- uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+ uint64_t max_interval = uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec"));
search_time_msec = now;
if (diff > max_interval) {
@@ -1831,9 +1831,6 @@ void ItemList::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index")));
-
- GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers
}
ItemList::ItemList() {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index fb5ab9f923..8a77c39487 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -960,17 +960,30 @@ void LineEdit::_notification(int p_what) {
if (ime_text.length() == 0) {
// Normal caret.
CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column);
-
- if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) {
+ if (using_placeholder || (caret.l_caret == Rect2() && caret.t_caret == Rect2())) {
// No carets, add one at the start.
int h = theme_cache.font->get_height(theme_cache.font_size);
int y = style->get_offset().y + (y_area - h) / 2;
- if (rtl) {
- caret.l_dir = TextServer::DIRECTION_RTL;
- caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
- } else {
- caret.l_dir = TextServer::DIRECTION_LTR;
- caret.l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
+ caret.l_dir = (rtl) ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
+ if (rtl) {
+ caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
+ } else {
+ caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h));
+ }
+ } break;
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ caret.l_caret = Rect2(Vector2(size.x / 2, y), Size2(caret_width, h));
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ if (rtl) {
+ caret.l_caret = Rect2(Vector2(style->get_offset().x, y), Size2(caret_width, h));
+ } else {
+ caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
+ }
+ } break;
}
RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color);
} else {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 2cbece69f2..0940b4c07b 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -451,7 +451,7 @@ void OptionButton::_queue_refresh_cache() {
}
cache_refresh_pending = true;
- callable_mp(this, &OptionButton::_refresh_size_cache).call_deferredp(nullptr, 0);
+ callable_mp(this, &OptionButton::_refresh_size_cache).call_deferred();
}
void OptionButton::select(int p_idx) {
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 9a411ef7ed..ab74979777 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -472,7 +472,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (allow_search && k.is_valid() && k->get_unicode() && k->is_pressed()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
- uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000));
+ uint64_t max_interval = uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec"));
search_time_msec = now;
if (diff > max_interval) {
@@ -558,7 +558,7 @@ void PopupMenu::_draw_items() {
check_ofs += theme_cache.h_separation;
}
- Point2 ofs = Point2();
+ Point2 ofs;
// Loop through all items and draw each.
for (int i = 0; i < items.size(); i++) {
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 2d2b3e413d..27002fad38 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -80,6 +80,15 @@ void Range::Shared::emit_changed(const char *p_what) {
}
void Range::set_value(double p_val) {
+ double prev_val = shared->val;
+ set_value_no_signal(p_val);
+
+ if (shared->val != prev_val) {
+ shared->emit_value_changed();
+ }
+}
+
+void Range::set_value_no_signal(double p_val) {
if (shared->step > 0) {
p_val = Math::round(p_val / shared->step) * shared->step;
}
@@ -101,8 +110,6 @@ void Range::set_value(double p_val) {
}
shared->val = p_val;
-
- shared->emit_value_changed();
}
void Range::set_min(double p_min) {
@@ -267,6 +274,7 @@ void Range::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_page"), &Range::get_page);
ClassDB::bind_method(D_METHOD("get_as_ratio"), &Range::get_as_ratio);
ClassDB::bind_method(D_METHOD("set_value", "value"), &Range::set_value);
+ ClassDB::bind_method(D_METHOD("set_value_no_signal", "value"), &Range::set_value_no_signal);
ClassDB::bind_method(D_METHOD("set_min", "minimum"), &Range::set_min);
ClassDB::bind_method(D_METHOD("set_max", "maximum"), &Range::set_max);
ClassDB::bind_method(D_METHOD("set_step", "step"), &Range::set_step);
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 19452243cf..f804155dec 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -72,6 +72,7 @@ protected:
public:
void set_value(double p_val);
+ void set_value_no_signal(double p_val);
void set_min(double p_min);
void set_max(double p_max);
void set_step(double p_step);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 7f487175dc..87cc26187a 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4057,7 +4057,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
Color color = theme_cache.default_color;
Color outline_color = theme_cache.font_outline_color;
int outline_size = theme_cache.outline_size;
- Rect2 dropcap_margins = Rect2();
+ Rect2 dropcap_margins;
for (int i = 0; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=");
@@ -5716,11 +5716,11 @@ Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_id
}
Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
- Dictionary d = Dictionary();
+ Dictionary d;
for (int i = 0; i < p_expressions.size(); i++) {
String expression = p_expressions[i];
- Array a = Array();
+ Array a;
Vector<String> parts = expression.split("=", true);
String key = parts[0];
if (parts.size() != 2) {
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 13ca62d7ff..d62acd52af 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -72,7 +72,7 @@ class ScrollBar : public Range {
NodePath drag_node_path;
bool drag_node_enabled = true;
- Vector2 drag_node_speed = Vector2();
+ Vector2 drag_node_speed;
Vector2 drag_node_accum;
Vector2 drag_node_from;
Vector2 last_drag_node_accum;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index a6aa4707ff..531226f938 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -107,45 +107,65 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
double prev_v_scroll = v_scroll->get_value();
double prev_h_scroll = h_scroll->get_value();
+ bool h_scroll_enabled = horizontal_scroll_mode != SCROLL_MODE_DISABLED;
+ bool v_scroll_enabled = vertical_scroll_mode != SCROLL_MODE_DISABLED;
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
- if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
- // only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) {
- h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor());
- } else if (v_scroll->is_visible_in_tree()) {
- v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8 * mb->get_factor());
+ if (mb->is_pressed()) {
+ bool scroll_value_modified = false;
+
+ bool v_scroll_hidden = !v_scroll->is_visible() && vertical_scroll_mode != SCROLL_MODE_SHOW_NEVER;
+ if (mb->get_button_index() == MouseButton::WHEEL_UP) {
+ // By default, the vertical orientation takes precedence. This is an exception.
+ if ((h_scroll_enabled && mb->is_shift_pressed()) || v_scroll_hidden) {
+ h_scroll->set_value(prev_h_scroll - h_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ } else if (v_scroll_enabled) {
+ v_scroll->set_value(prev_v_scroll - v_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ }
}
- }
-
- if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
- // only horizontal is enabled, scroll horizontally
- if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) {
- h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor());
- } else if (v_scroll->is_visible()) {
- v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8 * mb->get_factor());
+ if (mb->get_button_index() == MouseButton::WHEEL_DOWN) {
+ if ((h_scroll_enabled && mb->is_shift_pressed()) || v_scroll_hidden) {
+ h_scroll->set_value(prev_h_scroll + h_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ } else if (v_scroll_enabled) {
+ v_scroll->set_value(prev_v_scroll + v_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ }
}
- }
- if (mb->get_button_index() == MouseButton::WHEEL_LEFT && mb->is_pressed()) {
- if (h_scroll->is_visible_in_tree()) {
- h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8);
+ bool h_scroll_hidden = !h_scroll->is_visible() && horizontal_scroll_mode != SCROLL_MODE_SHOW_NEVER;
+ if (mb->get_button_index() == MouseButton::WHEEL_LEFT) {
+ // By default, the horizontal orientation takes precedence. This is an exception.
+ if ((v_scroll_enabled && mb->is_shift_pressed()) || h_scroll_hidden) {
+ v_scroll->set_value(prev_v_scroll - v_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ } else if (h_scroll_enabled) {
+ h_scroll->set_value(prev_h_scroll - h_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ }
}
- }
-
- if (mb->get_button_index() == MouseButton::WHEEL_RIGHT && mb->is_pressed()) {
- if (h_scroll->is_visible_in_tree()) {
- h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * mb->get_factor() / 8);
+ if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) {
+ if ((v_scroll_enabled && mb->is_shift_pressed()) || h_scroll_hidden) {
+ v_scroll->set_value(prev_v_scroll + v_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ } else if (h_scroll_enabled) {
+ h_scroll->set_value(prev_h_scroll + h_scroll->get_page() / 8 * mb->get_factor());
+ scroll_value_modified = true;
+ }
}
- }
- if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
- accept_event(); //accept event if scroll changed
+ if (scroll_value_modified && (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)) {
+ accept_event(); // Accept event if scroll changed.
+ return;
+ }
}
- if (!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()))) {
+ bool screen_is_touchscreen = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
+ if (!screen_is_touchscreen) {
return;
}
@@ -161,15 +181,13 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
drag_speed = Vector2();
drag_accum = Vector2();
last_drag_accum = Vector2();
- drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
- drag_touching = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
+ drag_from = Vector2(prev_h_scroll, prev_v_scroll);
+ drag_touching = true;
drag_touching_deaccel = false;
beyond_deadzone = false;
time_since_motion = 0;
- if (drag_touching) {
- set_physics_process_internal(true);
- time_since_motion = 0;
- }
+ set_physics_process_internal(true);
+ time_since_motion = 0;
} else {
if (drag_touching) {
@@ -180,6 +198,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
}
+ return;
}
Ref<InputEventMouseMotion> mm = p_gui_input;
@@ -189,22 +208,22 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = mm->get_relative();
drag_accum -= motion;
- if (beyond_deadzone || (horizontal_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.x) > deadzone) || (vertical_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.y) > deadzone)) {
+ if (beyond_deadzone || (h_scroll_enabled && Math::abs(drag_accum.x) > deadzone) || (v_scroll_enabled && Math::abs(drag_accum.y) > deadzone)) {
if (!beyond_deadzone) {
propagate_notification(NOTIFICATION_SCROLL_BEGIN);
emit_signal(SNAME("scroll_started"));
beyond_deadzone = true;
- // resetting drag_accum here ensures smooth scrolling after reaching deadzone
+ // Resetting drag_accum here ensures smooth scrolling after reaching deadzone.
drag_accum = -motion;
}
Vector2 diff = drag_from + drag_accum;
- if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
+ if (h_scroll_enabled) {
h_scroll->set_value(diff.x);
} else {
drag_accum.x = 0;
}
- if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
+ if (v_scroll_enabled) {
v_scroll->set_value(diff.y);
} else {
drag_accum.y = 0;
@@ -212,20 +231,26 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
time_since_motion = 0;
}
}
+
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
+ accept_event(); // Accept event if scroll changed.
+ }
+ return;
}
Ref<InputEventPanGesture> pan_gesture = p_gui_input;
if (pan_gesture.is_valid()) {
- if (h_scroll->is_visible_in_tree()) {
- h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8);
+ if (h_scroll_enabled) {
+ h_scroll->set_value(prev_h_scroll + h_scroll->get_page() * pan_gesture->get_delta().x / 8);
}
- if (v_scroll->is_visible_in_tree()) {
- v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
+ if (v_scroll_enabled) {
+ v_scroll->set_value(prev_v_scroll + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
}
- }
- if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
- accept_event(); //accept event if scroll changed
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
+ accept_event(); // Accept event if scroll changed.
+ }
+ return;
}
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index ab4808d312..9105267ad3 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -345,7 +345,7 @@ Vector<Control *> TabContainer::_get_tab_controls() const {
Vector<Control *> controls;
for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
- if (!control || control->is_set_as_top_level() || control == tab_bar || control == child_removing) {
+ if (!control || control->is_set_as_top_level() || control == tab_bar || children_removing.has(control)) {
continue;
}
@@ -584,10 +584,10 @@ void TabContainer::remove_child_notify(Node *p_child) {
int idx = get_tab_idx_from_control(c);
- // Before this, the tab control has not changed; after this, the tab control has changed.
- child_removing = p_child;
+ // As the child hasn't been removed yet, keep track of it so when the "tab_changed" signal is fired it can be ignored.
+ children_removing.push_back(c);
tab_bar->remove_tab(idx);
- child_removing = nullptr;
+ children_removing.erase(c);
_update_margins();
if (get_tab_count() == 0) {
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index b552aa459b..60961e02d3 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -46,7 +46,7 @@ class TabContainer : public Container {
bool drag_to_rearrange_enabled = false;
bool use_hidden_tabs_for_min_size = false;
bool theme_changing = false;
- Node *child_removing = nullptr;
+ Vector<Control *> children_removing;
struct ThemeCache {
int side_margin = 0;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 144aa2a1ef..56f7281721 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -833,7 +833,7 @@ void TextEdit::_notification(int p_what) {
continue;
}
- // If we've changed colour we are at the start of a new section, therefore we need to go back to the end
+ // If we've changed color we are at the start of a new section, therefore we need to go back to the end
// of the previous section to draw it, we'll also add the character back on.
if (color != previous_color) {
characters--;
@@ -1854,6 +1854,12 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (mb->get_button_index() == MouseButton::LEFT) {
if (selection_drag_attempt && is_mouse_over_selection()) {
+ remove_secondary_carets();
+
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ set_caret_line(pos.y, false, true, 0, 0);
+ set_caret_column(pos.x, true, 0);
+
deselect();
}
dragging_minimap = false;
@@ -2051,7 +2057,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
if (is_shortcut_keys_enabled()) {
- // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, CUT, COPY, PASTE.
+ // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE,
+ // CLEAR CARETS AND SELECTIONS, CUT, COPY, PASTE.
if (k->is_action("ui_text_select_all", true)) {
select_all();
accept_event();
@@ -2067,6 +2074,13 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
accept_event();
return;
}
+ if (k->is_action("ui_text_clear_carets_and_selection", true)) {
+ // Since the default shortcut is ESC, accepts the event only if it's actually performed.
+ if (_clear_carets_and_selection()) {
+ accept_event();
+ return;
+ }
+ }
if (k->is_action("ui_cut", true)) {
cut();
accept_event();
@@ -2651,7 +2665,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
set_caret_line(get_caret_line(caret_idx), false, true, 0, caret_idx);
set_caret_column(column, caret_idx == 0, caret_idx);
- // Now we can clean up the overlaping caret.
+ // Now we can clean up the overlapping caret.
if (overlapping_caret_index != -1) {
backspace(overlapping_caret_index);
i++;
@@ -2819,6 +2833,20 @@ void TextEdit::_move_caret_document_end(bool p_select) {
}
}
+bool TextEdit::_clear_carets_and_selection() {
+ if (get_caret_count() > 1) {
+ remove_secondary_carets();
+ return true;
+ }
+
+ if (has_selection()) {
+ deselect();
+ return true;
+ }
+
+ return false;
+}
+
void TextEdit::_get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x) const {
if (p_last_fit_x == -1) {
p_last_fit_x = _get_column_x_offset_for_line(p_old_column, p_old_line, p_old_column);
@@ -6538,7 +6566,7 @@ void TextEdit::_cut_internal(int p_caret) {
int indent_level = get_indent_level(cl);
double hscroll = get_h_scroll();
- // Check for overlaping carets.
+ // Check for overlapping carets.
// We don't need to worry about selections as that is caught before this entire section.
for (int j = i - 1; j >= 0; j--) {
if (get_caret_line(caret_edit_order[j]) == cl) {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 935f2a7ce8..92acbaf521 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -597,6 +597,7 @@ private:
void _delete(bool p_word = false, bool p_all_to_right = false);
void _move_caret_document_start(bool p_select);
void _move_caret_document_end(bool p_select);
+ bool _clear_carets_and_selection();
// Used in add_caret_at_carets
void _get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x = -1) const;
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index d9ab1c2c55..ad12757921 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -64,7 +64,7 @@ Size2 TextureButton::get_minimum_size() const {
bool TextureButton::has_point(const Point2 &p_point) const {
if (click_mask.is_valid()) {
Point2 point = p_point;
- Rect2 rect = Rect2();
+ Rect2 rect;
Size2 mask_size = click_mask->get_size();
if (!_position_rect.has_area()) {
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 6f9a9a5141..2da76883b4 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -4338,6 +4338,12 @@ TreeItem *Tree::get_selected() const {
return selected_item;
}
+void Tree::set_selected(TreeItem *p_item, int p_column) {
+ ERR_FAIL_INDEX(p_column, columns.size());
+ ERR_FAIL_COND(!p_item);
+ select_single_item(p_item, get_root(), p_column);
+}
+
int Tree::get_selected_column() const {
return selected_col;
}
@@ -4547,6 +4553,7 @@ void Tree::ensure_cursor_is_visible() {
return; // Nothing under cursor.
}
+ // Note: Code below similar to Tree::scroll_to_item(), in case of bug fix both.
const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size();
int y_offset = get_item_offset(selected_item);
@@ -4555,7 +4562,10 @@ void Tree::ensure_cursor_is_visible() {
y_offset -= tbh;
const int cell_h = compute_item_height(selected_item) + theme_cache.vseparation;
- const int screen_h = area_size.height - h_scroll->get_combined_minimum_size().height - tbh;
+ int screen_h = area_size.height - tbh;
+ if (h_scroll->is_visible()) {
+ screen_h -= h_scroll->get_combined_minimum_size().height;
+ }
if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet.
v_scroll->set_value(y_offset);
@@ -4706,26 +4716,32 @@ Point2 Tree::get_scroll() const {
void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
ERR_FAIL_NULL(p_item);
- if (!is_visible_in_tree() || !p_item->is_visible()) {
- return; // Hack to work around crash in get_item_rect() if Tree is not in tree.
- }
update_scrollbars();
- const real_t tree_height = get_size().y;
- const Rect2 item_rect = get_item_rect(p_item);
- const real_t item_y = item_rect.position.y;
- const real_t item_height = item_rect.size.y + theme_cache.vseparation;
+ // Note: Code below similar to Tree::ensure_cursor_is_visible(), in case of bug fix both.
+ const Size2 area_size = get_size() - theme_cache.panel_style->get_minimum_size();
- if (p_center_on_item) {
- v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f);
- } else {
- if (item_y < v_scroll->get_value()) {
- v_scroll->set_value(item_y);
+ int y_offset = get_item_offset(p_item);
+ if (y_offset != -1) {
+ const int tbh = _get_title_button_height();
+ y_offset -= tbh;
+
+ const int cell_h = compute_item_height(p_item) + theme_cache.vseparation;
+ int screen_h = area_size.height - tbh;
+ if (h_scroll->is_visible()) {
+ screen_h -= h_scroll->get_combined_minimum_size().height;
+ }
+
+ if (p_center_on_item) {
+ v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f);
} else {
- const real_t new_position = item_y + item_height - tree_height;
- if (new_position > v_scroll->get_value()) {
- v_scroll->set_value(new_position);
+ if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet.
+ v_scroll->set_value(y_offset);
+ } else if (y_offset + cell_h > v_scroll->get_value() + screen_h) {
+ v_scroll->set_value(y_offset - screen_h + cell_h);
+ } else if (y_offset < v_scroll->get_value()) {
+ v_scroll->set_value(y_offset);
}
}
}
@@ -4818,7 +4834,7 @@ TreeItem *Tree::get_item_with_text(const String &p_find) const {
void Tree::_do_incr_search(const String &p_add) {
uint64_t time = OS::get_singleton()->get_ticks_usec() / 1000; // convert to msec
uint64_t diff = time - last_keypress;
- if (diff > uint64_t(GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000))) {
+ if (diff > uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec"))) {
incr_search = p_add;
} else if (incr_search != p_add) {
incr_search += p_add;
@@ -5156,6 +5172,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
+ ClassDB::bind_method(D_METHOD("set_selected", "item", "column"), &Tree::set_selected);
ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index f994a5cec1..77a62e1d6a 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -666,6 +666,7 @@ public:
bool is_root_hidden() const;
TreeItem *get_next_selected(TreeItem *p_item);
TreeItem *get_selected() const;
+ void set_selected(TreeItem *p_item, int p_column = 0);
int get_selected_column() const;
int get_pressed_button() const;
void set_select_mode(SelectMode p_mode);
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 0d3389c13d..7bcd4721fc 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -219,6 +219,7 @@ void CanvasItem::_enter_canvas() {
}
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, canvas);
+ RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer);
canvas_group = "root_canvas" + itos(canvas.get_id());
@@ -236,6 +237,7 @@ void CanvasItem::_enter_canvas() {
canvas_layer = parent->canvas_layer;
RenderingServer::get_singleton()->canvas_item_set_parent(canvas_item, parent->get_canvas_item());
RenderingServer::get_singleton()->canvas_item_set_draw_index(canvas_item, get_index());
+ RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, visibility_layer);
}
pending_update = false;
@@ -977,6 +979,11 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_canvas_position_local", "screen_point"), &CanvasItem::make_canvas_position_local);
ClassDB::bind_method(D_METHOD("make_input_local", "event"), &CanvasItem::make_input_local);
+ ClassDB::bind_method(D_METHOD("set_visibility_layer", "layer"), &CanvasItem::set_visibility_layer);
+ ClassDB::bind_method(D_METHOD("get_visibility_layer"), &CanvasItem::get_visibility_layer);
+ ClassDB::bind_method(D_METHOD("set_visibility_layer_bit", "layer", "enabled"), &CanvasItem::set_visibility_layer_bit);
+ ClassDB::bind_method(D_METHOD("get_visibility_layer_bit", "layer"), &CanvasItem::get_visibility_layer_bit);
+
ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &CanvasItem::set_texture_filter);
ClassDB::bind_method(D_METHOD("get_texture_filter"), &CanvasItem::get_texture_filter);
@@ -996,6 +1003,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only,Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_layer", PROPERTY_HINT_LAYERS_2D_RENDER), "set_visibility_layer", "get_visibility_layer");
ADD_GROUP("Texture", "texture_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter");
@@ -1094,6 +1102,29 @@ int CanvasItem::get_canvas_layer() const {
}
}
+void CanvasItem::set_visibility_layer(uint32_t p_visibility_layer) {
+ visibility_layer = p_visibility_layer;
+ RenderingServer::get_singleton()->canvas_item_set_visibility_layer(canvas_item, p_visibility_layer);
+}
+
+uint32_t CanvasItem::get_visibility_layer() const {
+ return visibility_layer;
+}
+
+void CanvasItem::set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable) {
+ ERR_FAIL_UNSIGNED_INDEX(p_visibility_layer, 32);
+ if (p_enable) {
+ set_visibility_layer(visibility_layer | (1 << p_visibility_layer));
+ } else {
+ set_visibility_layer(visibility_layer & (~(1 << p_visibility_layer)));
+ }
+}
+
+bool CanvasItem::get_visibility_layer_bit(uint32_t p_visibility_layer) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_visibility_layer, 32, false);
+ return (visibility_layer & (1 << p_visibility_layer));
+}
+
void CanvasItem::_refresh_texture_filter_cache() {
if (!is_inside_tree()) {
return;
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 565ea930ce..4e78a175dc 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -89,6 +89,7 @@ private:
List<CanvasItem *>::Element *C = nullptr;
int light_mask = 1;
+ uint32_t visibility_layer = 1;
Window *window = nullptr;
bool visible = true;
@@ -223,6 +224,12 @@ public:
void set_self_modulate(const Color &p_self_modulate);
Color get_self_modulate() const;
+ void set_visibility_layer(uint32_t p_visibility_layer);
+ uint32_t get_visibility_layer() const;
+
+ void set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable);
+ bool get_visibility_layer_bit(uint32_t p_visibility_layer) const;
+
/* DRAWING API */
void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = 1.0, real_t p_dash = 2.0);
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 214efe432b..be5788739b 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -38,6 +38,7 @@ void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
if (viewport.is_valid()) {
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
+ vp->_gui_set_root_order_dirty();
}
}
diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp
index 946929aa89..8b4b98c172 100644
--- a/scene/main/multiplayer_api.cpp
+++ b/scene/main/multiplayer_api.cpp
@@ -39,7 +39,7 @@
#include "core/os/os.h"
#endif
-StringName MultiplayerAPI::default_interface = StringName();
+StringName MultiplayerAPI::default_interface;
void MultiplayerAPI::set_default_interface(const StringName &p_interface) {
ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface));
diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp
index 462dc1babb..92ba3debd1 100644
--- a/scene/main/multiplayer_peer.cpp
+++ b/scene/main/multiplayer_peer.cpp
@@ -94,6 +94,8 @@ void MultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
+ ClassDB::bind_method(D_METHOD("close"), &MultiplayerPeer::close);
+ ClassDB::bind_method(D_METHOD("disconnect_peer", "peer", "force"), &MultiplayerPeer::disconnect_peer, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
@@ -121,9 +123,6 @@ void MultiplayerPeer::_bind_methods() {
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
- ADD_SIGNAL(MethodInfo("server_disconnected"));
- ADD_SIGNAL(MethodInfo("connection_succeeded"));
- ADD_SIGNAL(MethodInfo("connection_failed"));
}
/*************/
@@ -213,6 +212,8 @@ void MultiplayerPeerExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_packet_peer);
GDVIRTUAL_BIND(_is_server);
GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_close);
+ GDVIRTUAL_BIND(_disconnect_peer, "p_peer", "p_force");
GDVIRTUAL_BIND(_get_unique_id);
GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
GDVIRTUAL_BIND(_is_refusing_new_connections);
diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h
index 63ce66871e..4b5909538e 100644
--- a/scene/main/multiplayer_peer.h
+++ b/scene/main/multiplayer_peer.h
@@ -82,9 +82,12 @@ public:
virtual TransferMode get_packet_mode() const = 0;
virtual int get_packet_channel() const = 0;
+ virtual void disconnect_peer(int p_peer, bool p_force = false) = 0;
+
virtual bool is_server() const = 0;
virtual void poll() = 0;
+ virtual void close() = 0;
virtual int get_unique_id() const = 0;
@@ -139,6 +142,8 @@ public:
EXBIND0RC(int, get_packet_channel);
EXBIND0RC(bool, is_server);
EXBIND0(poll);
+ EXBIND0(close);
+ EXBIND2(disconnect_peer, int, bool);
EXBIND0RC(int, get_unique_id);
EXBIND0RC(ConnectionStatus, get_connection_status);
};
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 304504a82e..5dd0911694 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1759,11 +1759,11 @@ void Node::add_to_group(const StringName &p_identifier, bool p_persistent) {
}
void Node::remove_from_group(const StringName &p_identifier) {
- ERR_FAIL_COND(!data.grouped.has(p_identifier));
-
HashMap<StringName, GroupData>::Iterator E = data.grouped.find(p_identifier);
- ERR_FAIL_COND(!E);
+ if (!E) {
+ return;
+ }
if (data.tree) {
data.tree->remove_from_group(E->key, this);
@@ -2581,10 +2581,14 @@ void Node::print_orphan_nodes() {
}
void Node::queue_free() {
+ // There are users which instantiate multiple scene trees for their games.
+ // Use the node's own tree to handle its deletion when relevant.
if (is_inside_tree()) {
get_tree()->queue_delete(this);
} else {
- SceneTree::get_singleton()->queue_delete(this);
+ SceneTree *tree = SceneTree::get_singleton();
+ ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available.");
+ tree->queue_delete(this);
}
}
@@ -2652,7 +2656,7 @@ PackedStringArray Node::get_configuration_warnings() const {
String Node::get_configuration_warnings_as_string() const {
PackedStringArray warnings = get_configuration_warnings();
- String all_warnings = String();
+ String all_warnings;
for (int i = 0; i < warnings.size(); i++) {
if (i > 0) {
all_warnings += "\n\n";
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 41eefe0f85..81a4e3073b 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -123,6 +123,9 @@ void SceneTree::tree_changed() {
void SceneTree::node_added(Node *p_node) {
emit_signal(node_added_name, p_node);
+ if (call_lock > 0) {
+ call_skip.erase(p_node->get_instance_id());
+ }
}
void SceneTree::node_removed(Node *p_node) {
@@ -131,7 +134,7 @@ void SceneTree::node_removed(Node *p_node) {
}
emit_signal(node_removed_name, p_node);
if (call_lock > 0) {
- call_skip.insert(p_node);
+ call_skip.insert(p_node->get_instance_id());
}
}
@@ -261,7 +264,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -275,7 +278,7 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -314,7 +317,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -327,7 +330,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -365,7 +368,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
if (p_call_flags & GROUP_CALL_REVERSE) {
for (int i = gr_node_count - 1; i >= 0; i--) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -378,7 +381,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group
} else {
for (int i = 0; i < gr_node_count; i++) {
- if (call_lock && call_skip.has(gr_nodes[i])) {
+ if (call_lock && call_skip.has(gr_nodes[i]->get_instance_id())) {
continue;
}
@@ -854,7 +857,7 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio
for (int i = 0; i < gr_node_count; i++) {
Node *n = gr_nodes[i];
- if (call_lock && call_skip.has(n)) {
+ if (call_lock && call_skip.has(n->get_instance_id())) {
continue;
}
@@ -904,7 +907,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal
}
Node *n = gr_nodes[i];
- if (call_lock && call_skip.has(n)) {
+ if (call_lock && call_skip.has(n->get_instance_id())) {
continue;
}
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index a460e40597..d4fcb288ae 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -135,7 +135,7 @@ private:
// Safety for when a node is deleted while a group is being called.
int call_lock = 0;
- HashSet<Node *> call_skip; // Skip erased nodes.
+ HashSet<ObjectID> call_skip; // Skip erased nodes. Store ID instead of pointer to avoid false positives when node is freed and a new node is allocated at the pointed address.
List<ObjectID> delete_queue;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 6af96a4147..4c2a761138 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -362,6 +362,7 @@ void Viewport::_notification(int p_what) {
current_canvas = find_world_2d()->get_canvas();
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
+ RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
_update_audio_listener_2d();
#ifndef _3D_DISABLED
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
@@ -993,11 +994,6 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
return;
}
- if (parent && parent->find_world_2d() == p_world_2d) {
- WARN_PRINT("Unable to use parent world_2d as world_2d");
- return;
- }
-
if (is_inside_tree()) {
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
}
@@ -1565,44 +1561,15 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
set_input_as_handled();
}
- if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
+ if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
// Alternate drop use (when using force_drag(), as proposed by #5342).
- gui.drag_successful = false;
- if (gui.mouse_focus) {
- gui.drag_successful = _gui_drop(gui.mouse_focus, pos, false);
- }
-
- gui.drag_data = Variant();
- gui.dragging = false;
-
- Control *drag_preview = _gui_get_drag_preview();
- if (drag_preview) {
- memdelete(drag_preview);
- gui.drag_preview_id = ObjectID();
- }
- _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
- get_base_window()->update_mouse_cursor_shape();
+ _perform_drop(gui.mouse_focus, pos);
}
_gui_cancel_tooltip();
} else {
- if (gui.drag_data.get_type() != Variant::NIL && mb->get_button_index() == MouseButton::LEFT) {
- gui.drag_successful = false;
- if (gui.drag_mouse_over) {
- gui.drag_successful = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false);
- }
-
- Control *drag_preview = _gui_get_drag_preview();
- if (drag_preview) {
- memdelete(drag_preview);
- gui.drag_preview_id = ObjectID();
- }
-
- gui.drag_data = Variant();
- gui.dragging = false;
- gui.drag_mouse_over = nullptr;
- _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
- get_base_window()->update_mouse_cursor_shape();
+ if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
+ _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos);
}
gui.mouse_focus_mask &= ~mouse_button_to_mask(mb->get_button_index()); // Remove from mask.
@@ -1686,7 +1653,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
gui.drag_attempted = true;
- if (gui.drag_data.get_type() != Variant::NIL) {
+ if (gui.dragging) {
_propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
}
}
@@ -1702,6 +1669,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_cancel_tooltip();
if (over) {
+ if (!gui.mouse_over) {
+ _drop_physics_mouseover();
+ }
_gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER);
gui.mouse_over = over;
}
@@ -1799,7 +1769,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (gui.drag_data.get_type() != Variant::NIL) {
+ if (gui.dragging) {
// Handle drag & drop.
Control *drag_preview = _gui_get_drag_preview();
@@ -2001,6 +1971,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (mm.is_null() && mb.is_null() && p_event->is_action_type()) {
+ if (gui.dragging && p_event->is_action_pressed("ui_cancel") && Input::get_singleton()->is_action_just_pressed("ui_cancel")) {
+ _perform_drop();
+ set_input_as_handled();
+ return;
+ }
+
if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
gui.key_focus->release_focus();
}
@@ -2082,6 +2058,27 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
+void Viewport::_perform_drop(Control *p_control, Point2 p_pos) {
+ // Without any arguments, simply cancel Drag and Drop.
+ if (p_control) {
+ gui.drag_successful = _gui_drop(p_control, p_pos, false);
+ } else {
+ gui.drag_successful = false;
+ }
+
+ Control *drag_preview = _gui_get_drag_preview();
+ if (drag_preview) {
+ memdelete(drag_preview);
+ gui.drag_preview_id = ObjectID();
+ }
+
+ gui.drag_data = Variant();
+ gui.dragging = false;
+ gui.drag_mouse_over = nullptr;
+ _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
+ get_base_window()->update_mouse_cursor_shape();
+}
+
void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
ERR_FAIL_COND(p_event.is_null());
@@ -2666,6 +2663,11 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
} else {
gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position());
if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) {
+ if (gui.subwindow_focused != sw.window) {
+ // Refocus.
+ _sub_window_grab_focus(sw.window);
+ }
+
gui.subwindow_resize_from_rect = r;
gui.subwindow_drag_from = mb->get_position();
gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
@@ -3045,8 +3047,6 @@ bool Viewport::gui_is_drag_successful() const {
}
void Viewport::set_input_as_handled() {
- _drop_physics_mouseover();
-
if (!handle_input_locally) {
ERR_FAIL_COND(!is_inside_tree());
Viewport *vp = this;
@@ -3249,6 +3249,29 @@ Transform2D Viewport::get_screen_transform() const {
return _get_input_pre_xform().affine_inverse() * get_final_transform();
}
+void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
+ canvas_cull_mask = p_canvas_cull_mask;
+ RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
+}
+
+uint32_t Viewport::get_canvas_cull_mask() const {
+ return canvas_cull_mask;
+}
+
+void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) {
+ ERR_FAIL_UNSIGNED_INDEX(p_layer, 32);
+ if (p_enable) {
+ set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer));
+ } else {
+ set_canvas_cull_mask(canvas_cull_mask & (~(1 << p_layer)));
+ }
+}
+
+bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const {
+ ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false);
+ return (canvas_cull_mask & (1 << p_layer));
+}
+
#ifndef _3D_DISABLED
AudioListener3D *Viewport::get_audio_listener_3d() const {
return audio_listener_3d;
@@ -3820,6 +3843,12 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
+ ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
+
+ ClassDB::bind_method(D_METHOD("set_canvas_cull_mask_bit", "layer", "enable"), &Viewport::set_canvas_cull_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_canvas_cull_mask_bit", "layer"), &Viewport::get_canvas_cull_mask_bit);
+
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat);
@@ -3925,6 +3954,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3);
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_canvas_cull_mask", "get_canvas_cull_mask");
ADD_SIGNAL(MethodInfo("size_changed"));
ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
@@ -4135,7 +4165,7 @@ DisplayServer::WindowID SubViewport::get_window_id() const {
}
Transform2D SubViewport::_stretch_transform() {
- Transform2D transform = Transform2D();
+ Transform2D transform;
Size2i view_size_2d_override = _get_size_2d_override();
if (size_2d_override_stretch && view_size_2d_override.width > 0 && view_size_2d_override.height > 0) {
Size2 scale = Size2(_get_size()) / Size2(view_size_2d_override);
@@ -4146,7 +4176,7 @@ Transform2D SubViewport::_stretch_transform() {
}
Transform2D SubViewport::get_screen_transform() const {
- Transform2D container_transform = Transform2D();
+ Transform2D container_transform;
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
if (c) {
if (c->is_stretch_enabled()) {
@@ -4205,6 +4235,8 @@ void SubViewport::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
}
-SubViewport::SubViewport() {}
+SubViewport::SubViewport() {
+ RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
+}
SubViewport::~SubViewport() {}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 6f67649ea3..dc69ec24d8 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -317,6 +317,8 @@ private:
SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT;
SDFScale sdf_scale = SDF_SCALE_50_PERCENT;
+ uint32_t canvas_cull_mask = 0xffffffff; // by default show everything
+
enum SubWindowDrag {
SUB_WINDOW_DRAG_DISABLED,
SUB_WINDOW_DRAG_MOVE,
@@ -403,6 +405,7 @@ private:
Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform);
void _gui_input_event(Ref<InputEvent> p_event);
+ void _perform_drop(Control *p_control = nullptr, Point2 p_pos = Point2());
void _gui_cleanup_internal_state(Ref<InputEvent> p_event);
_FORCE_INLINE_ Transform2D _get_input_pre_xform() const;
@@ -639,6 +642,12 @@ public:
void pass_mouse_focus_to(Viewport *p_viewport, Control *p_control);
+ void set_canvas_cull_mask(uint32_t p_layers);
+ uint32_t get_canvas_cull_mask() const;
+
+ void set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable);
+ bool get_canvas_cull_mask_bit(uint32_t p_layer) const;
+
virtual Transform2D get_screen_transform() const;
#ifndef _3D_DISABLED
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index ebb11ecee8..aff2c594d9 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1633,7 +1633,7 @@ void Window::_validate_property(PropertyInfo &p_property) const {
}
Transform2D Window::get_screen_transform() const {
- Transform2D embedder_transform = Transform2D();
+ Transform2D embedder_transform;
if (_get_embedder()) {
embedder_transform.translate_local(get_position());
embedder_transform = _get_embedder()->get_screen_transform() * embedder_transform;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index e536aeee51..0f55c7b3b8 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -1184,7 +1184,7 @@ void unregister_scene_types() {
ResourceLoader::remove_resource_format_loader(resource_loader_shader_include);
resource_loader_shader_include.unref();
- // StandardMaterial3D is not initialised when 3D is disabled, so it shouldn't be cleaned up either
+ // StandardMaterial3D is not initialized when 3D is disabled, so it shouldn't be cleaned up either
#ifndef _3D_DISABLED
BaseMaterial3D::finish_shaders();
PhysicalSkyMaterial::cleanup_shader();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index dfd9c6eb2f..ef619c9893 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -4964,7 +4964,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
if (rollback || best_frame == FRAME_MAX) {
// Commit the page if had to rollback or if no track was found
- print_animc("\tCommiting page..");
+ print_animc("\tCommiting page...");
// The end frame for the page depends entirely on whether its valid or
// no more keys were found.
@@ -5851,18 +5851,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
return dst;
}
case Variant::PACKED_INT32_ARRAY: {
- const Vector<int32_t> *arr_a = Object::cast_to<Vector<int32_t>>(a);
- const Vector<int32_t> *arr_b = Object::cast_to<Vector<int32_t>>(b);
- int32_t sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<int32_t> arr_a = a;
+ const Vector<int32_t> arr_b = b;
+ int32_t sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<int32_t> v;
v.resize(sz);
{
int32_t *vw = v.ptrw();
- const int32_t *ar = arr_a->ptr();
- const int32_t *br = arr_b->ptr();
+ const int32_t *ar = arr_a.ptr();
+ const int32_t *br = arr_b.ptr();
Variant va;
for (int32_t i = 0; i < sz; i++) {
@@ -5874,18 +5874,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_INT64_ARRAY: {
- const Vector<int64_t> *arr_a = Object::cast_to<Vector<int64_t>>(a);
- const Vector<int64_t> *arr_b = Object::cast_to<Vector<int64_t>>(b);
- int64_t sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<int64_t> arr_a = a;
+ const Vector<int64_t> arr_b = b;
+ int64_t sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<int64_t> v;
v.resize(sz);
{
int64_t *vw = v.ptrw();
- const int64_t *ar = arr_a->ptr();
- const int64_t *br = arr_b->ptr();
+ const int64_t *ar = arr_a.ptr();
+ const int64_t *br = arr_b.ptr();
Variant va;
for (int64_t i = 0; i < sz; i++) {
@@ -5897,18 +5897,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_FLOAT32_ARRAY: {
- const Vector<float> *arr_a = Object::cast_to<Vector<float>>(a);
- const Vector<float> *arr_b = Object::cast_to<Vector<float>>(b);
- int sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<float> arr_a = a;
+ const Vector<float> arr_b = b;
+ int sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<float> v;
v.resize(sz);
{
float *vw = v.ptrw();
- const float *ar = arr_a->ptr();
- const float *br = arr_b->ptr();
+ const float *ar = arr_a.ptr();
+ const float *br = arr_b.ptr();
Variant va;
for (int i = 0; i < sz; i++) {
@@ -5920,18 +5920,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_FLOAT64_ARRAY: {
- const Vector<double> *arr_a = Object::cast_to<Vector<double>>(a);
- const Vector<double> *arr_b = Object::cast_to<Vector<double>>(b);
- int sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<double> arr_a = a;
+ const Vector<double> arr_b = b;
+ int sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<double> v;
v.resize(sz);
{
double *vw = v.ptrw();
- const double *ar = arr_a->ptr();
- const double *br = arr_b->ptr();
+ const double *ar = arr_a.ptr();
+ const double *br = arr_b.ptr();
Variant va;
for (int i = 0; i < sz; i++) {
@@ -5943,18 +5943,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_VECTOR2_ARRAY: {
- const Vector<Vector2> *arr_a = Object::cast_to<Vector<Vector2>>(a);
- const Vector<Vector2> *arr_b = Object::cast_to<Vector<Vector2>>(b);
- int sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<Vector2> arr_a = a;
+ const Vector<Vector2> arr_b = b;
+ int sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<Vector2> v;
v.resize(sz);
{
Vector2 *vw = v.ptrw();
- const Vector2 *ar = arr_a->ptr();
- const Vector2 *br = arr_b->ptr();
+ const Vector2 *ar = arr_a.ptr();
+ const Vector2 *br = arr_b.ptr();
for (int i = 0; i < sz; i++) {
vw[i] = ar[i].lerp(br[i], c);
@@ -5964,18 +5964,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_VECTOR3_ARRAY: {
- const Vector<Vector3> *arr_a = Object::cast_to<Vector<Vector3>>(a);
- const Vector<Vector3> *arr_b = Object::cast_to<Vector<Vector3>>(b);
- int sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<Vector3> arr_a = a;
+ const Vector<Vector3> arr_b = b;
+ int sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<Vector3> v;
v.resize(sz);
{
Vector3 *vw = v.ptrw();
- const Vector3 *ar = arr_a->ptr();
- const Vector3 *br = arr_b->ptr();
+ const Vector3 *ar = arr_a.ptr();
+ const Vector3 *br = arr_b.ptr();
for (int i = 0; i < sz; i++) {
vw[i] = ar[i].lerp(br[i], c);
@@ -5985,18 +5985,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float
}
}
case Variant::PACKED_COLOR_ARRAY: {
- const Vector<Color> *arr_a = Object::cast_to<Vector<Color>>(a);
- const Vector<Color> *arr_b = Object::cast_to<Vector<Color>>(b);
- int sz = arr_a->size();
- if (sz == 0 || arr_b->size() != sz) {
+ const Vector<Color> arr_a = a;
+ const Vector<Color> arr_b = b;
+ int sz = arr_a.size();
+ if (sz == 0 || arr_b.size() != sz) {
return a;
} else {
Vector<Color> v;
v.resize(sz);
{
Color *vw = v.ptrw();
- const Color *ar = arr_a->ptr();
- const Color *br = arr_b->ptr();
+ const Color *ar = arr_a.ptr();
+ const Color *br = arr_b.ptr();
for (int i = 0; i < sz; i++) {
vw[i] = ar[i].lerp(br[i], c);
diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp
index dfaf82f36a..5698e61004 100644
--- a/scene/resources/bone_map.cpp
+++ b/scene/resources/bone_map.cpp
@@ -93,7 +93,7 @@ void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const Strin
}
StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const {
- StringName profile_bone_name = StringName();
+ StringName profile_bone_name;
HashMap<StringName, StringName>::ConstIterator E = bone_map.begin();
while (E) {
if (E->value == p_skeleton_bone_name) {
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 49b78a091d..eda9af9dde 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -936,6 +936,46 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const {
}
}
+Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const {
+ real_t path_length = get_baked_length(); // Ensure baked.
+ ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0.");
+
+ Vector2 pos = sample_baked(p_offset, p_cubic);
+
+ real_t ahead = p_offset + p_lookahead;
+
+ if (p_loop && ahead >= path_length) {
+ // If our lookahead will loop, we need to check if the path is closed.
+ int point_count = get_point_count();
+ if (point_count > 0) {
+ Vector2 start_point = get_point_position(0);
+ Vector2 end_point = get_point_position(point_count - 1);
+ if (start_point == end_point) {
+ // Since the path is closed we want to 'smooth off'
+ // the corner at the start/end.
+ // So we wrap the lookahead back round.
+ ahead = Math::fmod(ahead, path_length);
+ }
+ }
+ }
+
+ Vector2 ahead_pos = sample_baked(ahead, p_cubic);
+
+ Vector2 tangent_to_curve;
+ if (ahead_pos == pos) {
+ // This will happen at the end of non-looping or non-closed paths.
+ // We'll try a look behind instead, in order to get a meaningful angle.
+ tangent_to_curve =
+ (pos - sample_baked(p_offset - p_lookahead, p_cubic)).normalized();
+ } else {
+ tangent_to_curve = (ahead_pos - pos).normalized();
+ }
+
+ Vector2 normal_of_curve = -tangent_to_curve.orthogonal();
+
+ return Transform2D(normal_of_curve, tangent_to_curve, pos);
+}
+
PackedVector2Array Curve2D::get_baked_points() const {
if (baked_cache_dirty) {
_bake();
@@ -1184,6 +1224,7 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "loop", "lookahead"), &Curve2D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(true), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point);
ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset);
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 88b6dda096..fa1d35aab1 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -216,6 +216,7 @@ public:
real_t get_baked_length() const;
Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const;
+ Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_loop = true, real_t p_lookahead = 4.0) const;
PackedVector2Array get_baked_points() const; //useful for going through
Vector2 get_closest_point(const Vector2 &p_to_point) const;
real_t get_closest_offset(const Vector2 &p_to_point) const;
diff --git a/scene/resources/default_theme/color_picker_hue.svg b/scene/resources/default_theme/color_picker_hue.svg
deleted file mode 100644
index ff75d5eb9e..0000000000
--- a/scene/resources/default_theme/color_picker_hue.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 1 256" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(0 256 -256 0 0 0)" gradientUnits="userSpaceOnUse" x1="0" x2="1" y1="0" y2="0"><stop offset="0" stop-color="#f00"/><stop offset=".04" stop-color="#ff4000"/><stop offset=".08" stop-color="#ff8000"/><stop offset=".17" stop-color="#ff0"/><stop offset=".25" stop-color="#80ff00"/><stop offset=".33" stop-color="#0f0"/><stop offset=".42" stop-color="#00ff80"/><stop offset=".5" stop-color="#0ff"/><stop offset=".58" stop-color="#0080ff"/><stop offset=".63" stop-color="#0040ff"/><stop offset=".67" stop-color="#00f"/><stop offset=".75" stop-color="#8000ff"/><stop offset=".83" stop-color="#f0f"/><stop offset=".92" stop-color="#ff0080"/><stop offset="1" stop-color="#f00"/></linearGradient><path d="m0 0h1v256h-1z" fill="url(#a)"/></svg>
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index aa271e6f4b..894936acd7 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -887,12 +887,65 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("shape_rect", "ColorPicker", icons["picker_shape_rectangle"]);
theme->set_icon("shape_rect_wheel", "ColorPicker", icons["picker_shape_rectangle_wheel"]);
theme->set_icon("add_preset", "ColorPicker", icons["add"]);
- theme->set_icon("color_hue", "ColorPicker", icons["color_picker_hue"]);
theme->set_icon("sample_bg", "ColorPicker", icons["mini_checkerboard"]);
theme->set_icon("overbright_indicator", "ColorPicker", icons["color_picker_overbright"]);
theme->set_icon("bar_arrow", "ColorPicker", icons["color_picker_bar_arrow"]);
theme->set_icon("picker_cursor", "ColorPicker", icons["color_picker_cursor"]);
+ {
+ const int precision = 7;
+
+ Ref<Gradient> hue_gradient;
+ hue_gradient.instantiate();
+ PackedFloat32Array offsets;
+ offsets.resize(precision);
+ PackedColorArray colors;
+ colors.resize(precision);
+
+ for (int i = 0; i < precision; i++) {
+ float h = i / float(precision - 1);
+ offsets.write[i] = h;
+ colors.write[i] = Color::from_hsv(h, 1, 1);
+ }
+ hue_gradient->set_offsets(offsets);
+ hue_gradient->set_colors(colors);
+
+ Ref<GradientTexture2D> hue_texture;
+ hue_texture.instantiate();
+ hue_texture->set_width(800);
+ hue_texture->set_height(6);
+ hue_texture->set_gradient(hue_gradient);
+
+ theme->set_icon("color_hue", "ColorPicker", hue_texture);
+ }
+
+ {
+ const int precision = 7;
+
+ Ref<Gradient> hue_gradient;
+ hue_gradient.instantiate();
+ PackedFloat32Array offsets;
+ offsets.resize(precision);
+ PackedColorArray colors;
+ colors.resize(precision);
+
+ for (int i = 0; i < precision; i++) {
+ float h = i / float(precision - 1);
+ offsets.write[i] = h;
+ colors.write[i] = Color::from_ok_hsl(h, 1, 0.5);
+ }
+ hue_gradient->set_offsets(offsets);
+ hue_gradient->set_colors(colors);
+
+ Ref<GradientTexture2D> hue_texture;
+ hue_texture.instantiate();
+ hue_texture->set_width(800);
+ hue_texture->set_height(6);
+ hue_texture->set_gradient(hue_gradient);
+
+ theme->set_icon("color_okhsl_hue", "ColorPicker", hue_texture);
+ }
+
// ColorPickerButton
theme->set_icon("bg", "ColorPickerButton", icons["mini_checkerboard"]);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index cbecab62b3..84814d939b 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1003,11 +1003,11 @@ void FontFile::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD Subpixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name");
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic,Fixed Size", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
@@ -1341,6 +1341,19 @@ void FontFile::reset_state() {
/*************************************************************************/
+// OEM encoding mapping for 0x80..0xFF range.
+static const char32_t _oem_to_unicode[][129] = {
+ U"\u20ac\ufffe\u201a\ufffe\u201e\u2026\u2020\u2021\ufffe\u2030\u0160\u2039\u015a\u0164\u017d\u0179\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffe\u2122\u0161\u203a\u015b\u0165\u017e\u017a\xa0\u02c7\u02d8\u0141\xa4\u0104\xa6\xa7\xa8\xa9\u015e\xab\xac\xad\xae\u017b\xb0\xb1\u02db\u0142\xb4\xb5\xb6\xb7\xb8\u0105\u015f\xbb\u013d\u02dd\u013e\u017c\u0154\xc1\xc2\u0102\xc4\u0139\u0106\xc7\u010c\xc9\u0118\xcb\u011a\xcd\xce\u010e\u0110\u0143\u0147\xd3\xd4\u0150\xd6\xd7\u0158\u016e\xda\u0170\xdc\xdd\u0162\xdf\u0155\xe1\xe2\u0103\xe4\u013a\u0107\xe7\u010d\xe9\u0119\xeb\u011b\xed\xee\u010f\u0111\u0144\u0148\xf3\xf4\u0151\xf6\xf7\u0159\u016f\xfa\u0171\xfc\xfd\u0163\u02d9", // 1250 - Latin 2
+ U"\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffe\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab\xac\xad\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5\xb6\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f", // 1251 - Cyrillic
+ U"\u20ac\ufffe\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160\u2039\u0152\ufffe\u017d\ufffe\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u0161\u203a\u0153\ufffe\u017e\u0178\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", // 1252 - Latin 1
+ U"\u20ac\ufffe\u201a\u0192\u201e\u2026\u2020\u2021\ufffe\u2030\ufffe\u2039\ufffe\ufffe\ufffe\ufffe\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffe\u2122\ufffe\u203a\ufffe\ufffe\ufffe\ufffe\xa0\u0385\u0386\xa3\xa4\xa5\xa6\xa7\xa8\xa9\ufffe\xab\xac\xad\xae\u2015\xb0\xb1\xb2\xb3\u0384\xb5\xb6\xb7\u0388\u0389\u038a\xbb\u038c\xbd\u038e\u038f\u0390\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\ufffe\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\ufffe", // 1253 - Greek
+ U"\u20ac\ufffe\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0160\u2039\u0152\ufffe\ufffe\ufffe\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\u0161\u203a\u0153\ufffe\ufffe\u0178\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\u011e\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\u0130\u015e\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\u011f\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\u0131\u015f\xff", // 1254 - Turkish
+ U"\u20ac\ufffe\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\ufffe\u2039\ufffe\ufffe\ufffe\ufffe\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\ufffe\u203a\ufffe\ufffe\ufffe\ufffe\xa0\xa1\xa2\xa3\u20aa\xa5\xa6\xa7\xa8\xa9\xd7\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xf7\xbb\xbc\xbd\xbe\xbf\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\ufffe\u05bb\u05bc\u05bd\u05be\u05bf\u05c0\u05c1\u05c2\u05c3\u05f0\u05f1\u05f2\u05f3\u05f4\ufffe\ufffe\ufffe\ufffe\ufffe\ufffe\ufffe\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\ufffe\ufffe\u200e\u200f\ufffe", // 1255 - Hebrew
+ U"\u20ac\u067e\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\u0679\u2039\u0152\u0686\u0698\u0688\u06af\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u06a9\u2122\u0691\u203a\u0153\u200c\u200d\u06ba\xa0\u060c\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\u06be\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\u061b\xbb\xbc\xbd\xbe\u061f\u06c1\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\xd7\u0637\u0638\u0639\u063a\u0640\u0641\u0642\u0643\xe0\u0644\xe2\u0645\u0646\u0647\u0648\xe7\xe8\xe9\xea\xeb\u0649\u064a\xee\xef\u064b\u064c\u064d\u064e\xf4\u064f\u0650\xf7\u0651\xf9\u0652\xfb\xfc\u200e\u200f\u06d2", // 1256 - Arabic
+ U"\u20ac\ufffe\u201a\ufffe\u201e\u2026\u2020\u2021\ufffe\u2030\ufffe\u2039\ufffe\xa8\u02c7\xb8\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffe\u2122\ufffe\u203a\ufffe\xaf\u02db\ufffe\xa0\ufffe\xa2\xa3\xa4\ufffe\xa6\xa7\xd8\xa9\u0156\xab\xac\xad\xae\xc6\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xf8\xb9\u0157\xbb\xbc\xbd\xbe\xe6\u0104\u012e\u0100\u0106\xc4\xc5\u0118\u0112\u010c\xc9\u0179\u0116\u0122\u0136\u012a\u013b\u0160\u0143\u0145\xd3\u014c\xd5\xd6\xd7\u0172\u0141\u015a\u016a\xdc\u017b\u017d\xdf\u0105\u012f\u0101\u0107\xe4\xe5\u0119\u0113\u010d\xe9\u017a\u0117\u0123\u0137\u012b\u013c\u0161\u0144\u0146\xf3\u014d\xf5\xf6\xf7\u0173\u0142\u015b\u016b\xfc\u017c\u017e\u02d9", // 1257 - Baltic
+ U"\u20ac\ufffe\u201a\u0192\u201e\u2026\u2020\u2021\u02c6\u2030\ufffe\u2039\u0152\ufffe\ufffe\ufffe\ufffe\u2018\u2019\u201c\u201d\u2022\u2013\u2014\u02dc\u2122\ufffe\u203a\u0153\ufffe\ufffe\u0178\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\u0102\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\u0300\xcd\xce\xcf\u0110\xd1\u0309\xd3\xd4\u01a0\xd6\xd7\xd8\xd9\xda\xdb\xdc\u01af\u0303\xdf\xe0\xe1\xe2\u0103\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\u0301\xed\xee\xef\u0111\xf1\u0323\xf3\xf4\u01a1\xf6\xf7\xf8\xf9\xfa\xfb\xfc\u01b0\u20ab\xff", // 1258 - Vietnamese
+};
+
Error FontFile::load_bitmap_font(const String &p_path) {
reset_state();
@@ -1371,25 +1384,64 @@ Error FontFile::load_bitmap_font(const String &p_path) {
f->get_buffer((unsigned char *)&magic, 4);
if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
// Binary BMFont file.
- ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(RTR("Version %d of BMFont is not supported."), (int)magic[3]));
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(RTR("Version %d of BMFont is not supported (should be 3)."), (int)magic[3]));
uint8_t block_type = f->get_8();
uint32_t block_size = f->get_32();
+ bool unicode = false;
+ uint8_t encoding = 9;
while (!f->eof_reached()) {
uint64_t off = f->get_position();
switch (block_type) {
case 1: /* info */ {
ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size."));
base_size = f->get_16();
+ if (base_size <= 0) {
+ base_size = 16;
+ }
uint8_t flags = f->get_8();
- ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
if (flags & (1 << 3)) {
st_flags.set_flag(TextServer::FONT_BOLD);
}
if (flags & (1 << 2)) {
st_flags.set_flag(TextServer::FONT_ITALIC);
}
- f->get_8(); // non-unicode charset, skip
+ unicode = (flags & 0x02);
+ uint8_t encoding_id = f->get_8(); // non-unicode charset
+ if (!unicode) {
+ switch (encoding_id) {
+ case 0x00: {
+ encoding = 2;
+ } break;
+ case 0xB2: {
+ encoding = 6;
+ } break;
+ case 0xBA: {
+ encoding = 7;
+ } break;
+ case 0xEE: {
+ encoding = 0;
+ } break;
+ case 0xA1: {
+ encoding = 3;
+ } break;
+ case 0xB1: {
+ encoding = 5;
+ } break;
+ case 0xCC: {
+ encoding = 1;
+ } break;
+ case 0xA2: {
+ encoding = 4;
+ } break;
+ case 0xA3: {
+ encoding = 8;
+ } break;
+ default: {
+ WARN_PRINT(vformat("Unknown BMFont OEM encoding %x, parsing as Unicode (should be 0x00 - Latin 1, 0xB2 - Arabic, 0xBA - Baltic, 0xEE - Latin 2, 0xA1 - Greek, 0xB1 - Hebrew, 0xCC - Cyrillic, 0xA2 - Turkish, 0xA3 - Vietnamese).", encoding_id));
+ } break;
+ };
+ }
f->get_16(); // stretch_h, skip
f->get_8(); // aa, skip
f->get_32(); // padding, skip
@@ -1492,6 +1544,14 @@ Error FontFile::load_bitmap_font(const String &p_path) {
Rect2 uv_rect;
char32_t idx = f->get_32();
+ if (!unicode && encoding < 9) {
+ if (idx >= 0x80 && idx <= 0xFF) {
+ idx = _oem_to_unicode[encoding][idx - 0x80];
+ } else if (idx > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", idx));
+ idx = 0x00;
+ }
+ }
uv_rect.position.x = (int16_t)f->get_16();
uv_rect.position.y = (int16_t)f->get_16();
uv_rect.size.width = (int16_t)f->get_16();
@@ -1508,24 +1568,25 @@ Error FontFile::load_bitmap_font(const String &p_path) {
int texture_idx = f->get_8();
uint8_t channel = f->get_8();
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
int ch_off = 0;
- switch (channel) {
- case 1:
- ch_off = 2;
- break; // B
- case 2:
- ch_off = 1;
- break; // G
- case 4:
- ch_off = 0;
- break; // R
- case 8:
- ch_off = 3;
- break; // A
- default:
- ch_off = 0;
- break;
+ if (packed) {
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
}
set_glyph_advance(0, base_size, idx, advance);
set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
@@ -1546,6 +1607,20 @@ Error FontFile::load_bitmap_font(const String &p_path) {
Vector2i kpk;
kpk.x = f->get_32();
kpk.y = f->get_32();
+ if (!unicode && encoding < 9) {
+ if (kpk.x >= 0x80 && kpk.x <= 0xFF) {
+ kpk.x = _oem_to_unicode[encoding][kpk.x - 0x80];
+ } else if (kpk.x > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", kpk.x));
+ kpk.x = 0x00;
+ }
+ if (kpk.y >= 0x80 && kpk.y <= 0xFF) {
+ kpk.y = _oem_to_unicode[encoding][kpk.y - 0x80];
+ } else if (kpk.y > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", kpk.y));
+ kpk.y = 0x00;
+ }
+ }
set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
}
} break;
@@ -1561,6 +1636,8 @@ Error FontFile::load_bitmap_font(const String &p_path) {
} else {
// Text BMFont file.
f->seek(0);
+ bool unicode = false;
+ uint8_t encoding = 9;
while (true) {
String line = f->get_line();
@@ -1607,7 +1684,6 @@ Error FontFile::load_bitmap_font(const String &p_path) {
if (type == "info") {
if (keys.has("size")) {
base_size = keys["size"].to_int();
- set_fixed_size(base_size);
}
if (keys.has("outline")) {
outline = keys["outline"].to_int();
@@ -1625,7 +1701,38 @@ Error FontFile::load_bitmap_font(const String &p_path) {
if (keys.has("face")) {
font_name = keys["face"];
}
- ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, RTR("Non-unicode version of BMFont is not supported."));
+ if (keys.has("unicode")) {
+ unicode = keys["unicode"].to_int();
+ }
+ if (!unicode) {
+ if (keys.has("charset")) {
+ String encoding_name = keys["charset"].to_upper();
+ if (encoding_name == "" || encoding_name == "ASCII" || encoding_name == "ANSI") {
+ encoding = 2;
+ } else if (encoding_name == "ARABIC") {
+ encoding = 6;
+ } else if (encoding_name == "BALTIC") {
+ encoding = 7;
+ } else if (encoding_name == "EASTEUROPE") {
+ encoding = 0;
+ } else if (encoding_name == "GREEK") {
+ encoding = 3;
+ } else if (encoding_name == "HEBREW") {
+ encoding = 5;
+ } else if (encoding_name == "RUSSIAN") {
+ encoding = 1;
+ } else if (encoding_name == "TURKISH") {
+ encoding = 4;
+ } else if (encoding_name == "VIETNAMESE") {
+ encoding = 8;
+ } else {
+ WARN_PRINT(vformat("Unknown BMFont OEM encoding %s, parsing as Unicode (should be ANSI, ASCII, ARABIC, BALTIC, EASTEUROPE, GREEK, HEBREW, RUSSIAN, TURKISH or VIETNAMESE).", encoding_name));
+ }
+ } else {
+ encoding = 2;
+ }
+ }
+ set_fixed_size(base_size);
} else if (type == "common") {
if (keys.has("lineHeight")) {
height = keys["lineHeight"].to_int();
@@ -1684,7 +1791,10 @@ Error FontFile::load_bitmap_font(const String &p_path) {
ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format."));
}
} else {
- if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ if ((ch[3] == 0) && (ch[0] == 4) && (ch[1] == 4) && (ch[2] == 4) && img->get_format() == Image::FORMAT_RGBA8) { // might be RGBA8 color, no outline (color part of the image should be sold white, but some apps designed for Godot 3 generate color fonts with this config)
+ outline = 0;
+ set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
outline = 0;
ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format."));
set_texture_image(0, Vector2i(base_size, 0), page, img);
@@ -1719,6 +1829,14 @@ Error FontFile::load_bitmap_font(const String &p_path) {
if (keys.has("id")) {
idx = keys["id"].to_int();
+ if (!unicode && encoding < 9) {
+ if (idx >= 0x80 && idx <= 0xFF) {
+ idx = _oem_to_unicode[encoding][idx - 0x80];
+ } else if (idx > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", idx));
+ idx = 0x00;
+ }
+ }
}
if (keys.has("x")) {
uv_rect.position.x = keys["x"].to_int();
@@ -1753,24 +1871,25 @@ Error FontFile::load_bitmap_font(const String &p_path) {
channel = keys["chnl"].to_int();
}
- ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, RTR("Invalid glyph channel."));
int ch_off = 0;
- switch (channel) {
- case 1:
- ch_off = 2;
- break; // B
- case 2:
- ch_off = 1;
- break; // G
- case 4:
- ch_off = 0;
- break; // R
- case 8:
- ch_off = 3;
- break; // A
- default:
- ch_off = 0;
- break;
+ if (packed) {
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
}
set_glyph_advance(0, base_size, idx, advance);
set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
@@ -1791,6 +1910,20 @@ Error FontFile::load_bitmap_font(const String &p_path) {
if (keys.has("second")) {
kpk.y = keys["second"].to_int();
}
+ if (!unicode && encoding < 9) {
+ if (kpk.x >= 0x80 && kpk.x <= 0xFF) {
+ kpk.x = _oem_to_unicode[encoding][kpk.x - 0x80];
+ } else if (kpk.x > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", kpk.x));
+ kpk.x = 0x00;
+ }
+ if (kpk.y >= 0x80 && kpk.y <= 0xFF) {
+ kpk.y = _oem_to_unicode[encoding][kpk.y - 0x80];
+ } else if (kpk.y > 0xFF) {
+ WARN_PRINT(vformat("Invalid BMFont OEM character %x (should be 0x00-0xFF).", kpk.x));
+ kpk.y = 0x00;
+ }
+ }
if (keys.has("amount")) {
set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
}
@@ -2728,11 +2861,11 @@ void SystemFont::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names");
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD Subpixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks");
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index b728c24e0d..cec5569345 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -255,7 +255,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
}
#define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \
- Vector3 transformed_vert = Vector3(); \
+ Vector3 transformed_vert; \
for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \
int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \
float w = weight_array[vert_idx * bone_count + weight_idx]; \
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 706db3af16..d1e300e057 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -940,7 +940,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (int16_t)CLAMP(src[0] / 127.0f * 32767, -32768, 32767);
dst[1] = (int16_t)CLAMP(src[1] / 127.0f * 32767, -32768, 32767);
}
- src_offset += sizeof(int16_t) * 2;
+ src_offset += sizeof(int8_t) * 2;
} else {
for (uint32_t i = 0; i < p_elements; i++) {
const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
@@ -962,7 +962,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
- src_offset += sizeof(uint16_t) * 2;
+ src_offset += sizeof(uint8_t) * 4; // 1 byte padding
} else {
for (uint32_t i = 0; i < p_elements; i++) {
const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
@@ -973,7 +973,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
- src_offset += sizeof(uint16_t) * 2;
+ src_offset += sizeof(float) * 3;
}
}
@@ -988,7 +988,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (uint16_t)CLAMP((src[0] / 127.0f * .5f + .5f) * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP((src[1] / 127.0f * .5f + .5f) * 65535, 0, 65535);
}
- src_offset += sizeof(uint16_t) * 2;
+ src_offset += sizeof(uint8_t) * 2;
} else { // int16 SNORM -> uint16 UNORM
for (uint32_t i = 0; i < p_elements; i++) {
const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
@@ -1010,7 +1010,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
- src_offset += sizeof(uint16_t) * 2;
+ src_offset += sizeof(uint8_t) * 4;
} else {
for (uint32_t i = 0; i < p_elements; i++) {
const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset];
@@ -1021,7 +1021,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma
dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535);
dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535);
}
- src_offset += sizeof(uint16_t) * 2;
+ src_offset += sizeof(float) * 4;
}
}
} break;
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index eb83a37c7b..4c6d533c72 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -30,6 +30,7 @@
#include "primitive_meshes.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "scene/resources/theme.h"
#include "scene/theme/theme_db.h"
@@ -37,6 +38,8 @@
#include "thirdparty/misc/clipper.hpp"
#include "thirdparty/misc/polypartition.h"
+#define PADDING_REF_SIZE 1024.0
+
/**
PrimitiveMesh
*/
@@ -94,6 +97,26 @@ void PrimitiveMesh::_update() const {
}
}
+ if (add_uv2) {
+ // _create_mesh_array should populate our UV2, this is a fallback in case it doesn't.
+ // As we don't know anything about the geometry we only pad the right and bottom edge
+ // of our texture.
+ Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV];
+ Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2];
+
+ if (uv.size() > 0 && uv2.size() == 0) {
+ Vector2 uv2_scale = get_uv2_scale();
+ uv2.resize(uv.size());
+
+ Vector2 *uv2w = uv2.ptrw();
+ for (int i = 0; i < uv.size(); i++) {
+ uv2w[i] = uv[i] * uv2_scale;
+ }
+ }
+
+ arr[RS::ARRAY_TEX_UV2] = uv2;
+ }
+
array_len = pc;
index_array_len = indices.size();
// in with the new
@@ -160,7 +183,12 @@ TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) c
uint32_t PrimitiveMesh::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 | RS::ARRAY_FORMAT_INDEX;
+ uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX;
+ if (add_uv2) {
+ mesh_format |= RS::ARRAY_FORMAT_TEX_UV2;
+ }
+
+ return mesh_format;
}
Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const {
@@ -219,9 +247,17 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);
ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);
+ ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2);
+ ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2);
+
+ ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding);
+ ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding"), "set_uv2_padding", "get_uv2_padding");
GDVIRTUAL_BIND(_create_mesh_array);
}
@@ -233,7 +269,7 @@ void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid());
notify_property_list_changed();
emit_changed();
- };
+ }
}
Ref<Material> PrimitiveMesh::get_material() const {
@@ -263,6 +299,42 @@ bool PrimitiveMesh::get_flip_faces() const {
return flip_faces;
}
+void PrimitiveMesh::set_add_uv2(bool p_enable) {
+ add_uv2 = p_enable;
+ _update_lightmap_size();
+ _request_update();
+}
+
+void PrimitiveMesh::set_uv2_padding(float p_padding) {
+ uv2_padding = p_padding;
+ _update_lightmap_size();
+ _request_update();
+}
+
+Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const {
+ Vector2 uv2_scale;
+ Vector2 lightmap_size = get_lightmap_size_hint();
+
+ // Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size.
+ uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x);
+ uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y);
+
+ // Inverse it to turn our margin into a scale
+ uv2_scale = Vector2(1.0, 1.0) - uv2_scale;
+
+ return uv2_scale;
+}
+
+float PrimitiveMesh::get_lightmap_texel_size() const {
+ float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size");
+
+ if (texel_size <= 0.0) {
+ texel_size = 0.2;
+ }
+
+ return texel_size;
+}
+
PrimitiveMesh::PrimitiveMesh() {
mesh = RenderingServer::get_singleton()->mesh_create();
}
@@ -275,22 +347,52 @@ PrimitiveMesh::~PrimitiveMesh() {
CapsuleMesh
*/
+void CapsuleMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend
+ float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length
+
+ _lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding;
+ _lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding;
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void CapsuleMesh::_create_mesh_array(Array &p_arr) const {
- create_mesh_array(p_arr, radius, height, radial_segments, rings);
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding);
}
-void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) {
+void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings, bool p_add_uv2, const float p_uv2_padding) {
int i, j, prevrow, thisrow, point;
float x, y, z, u, v, w;
float onethird = 1.0 / 3.0;
float twothirds = 2.0 / 3.0;
+ // Only used if we calculate UV2
+ float radial_width = 2.0 * radius * Math_PI;
+ float radial_h = radial_width / (radial_width + p_uv2_padding);
+ float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend
+ float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length
+ float radial_v = radial_length / vertical_length; // v size of top and bottom section
+ float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section
+
// note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom
Vector<Vector3> points;
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
point = 0;
@@ -322,6 +424,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
normals.push_back(p.normalized());
ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v * onethird));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u * radial_h, v * radial_v));
+ }
point++;
if (i > 0 && j > 0) {
@@ -332,12 +437,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
- };
+ }
+ }
prevrow = thisrow;
thisrow = point;
- };
+ }
/* cylinder */
thisrow = point;
@@ -361,6 +466,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
normals.push_back(Vector3(x, 0.0, -z));
ADD_TANGENT(-z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, onethird + (v * onethird)));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v)));
+ }
point++;
if (i > 0 && j > 0) {
@@ -371,12 +479,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
- };
+ }
+ }
prevrow = thisrow;
thisrow = point;
- };
+ }
/* bottom hemisphere */
thisrow = point;
@@ -390,17 +498,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
y = radius * cos(0.5 * Math_PI * v);
for (i = 0; i <= radial_segments; i++) {
- float u2 = i;
- u2 /= radial_segments;
+ u = i;
+ u /= radial_segments;
- x = -sin(u2 * Math_TAU);
- z = cos(u2 * Math_TAU);
+ x = -sin(u * Math_TAU);
+ z = cos(u * Math_TAU);
Vector3 p = Vector3(x * radius * w, y, -z * radius * w);
points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0));
normals.push_back(p.normalized());
ADD_TANGENT(-z, 0.0, -x, 1.0)
- uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird)));
+ uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird)));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v)));
+ }
point++;
if (i > 0 && j > 0) {
@@ -411,17 +522,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
- };
+ }
+ }
prevrow = thisrow;
thisrow = point;
- };
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (p_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -450,6 +564,7 @@ void CapsuleMesh::set_radius(const float p_radius) {
if (radius > height * 0.5) {
height = radius * 2.0;
}
+ _update_lightmap_size();
_request_update();
}
@@ -462,6 +577,7 @@ void CapsuleMesh::set_height(const float p_height) {
if (radius > height * 0.5) {
radius = height * 0.5;
}
+ _update_lightmap_size();
_request_update();
}
@@ -493,16 +609,53 @@ CapsuleMesh::CapsuleMesh() {}
BoxMesh
*/
+void BoxMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ float width = (size.x + size.z) / texel_size;
+ float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size;
+
+ _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;
+ _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void BoxMesh::_create_mesh_array(Array &p_arr) const {
- BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d);
+ // Note about padding, with our box each face of the box faces a different direction so we want a seam
+ // around every face. We thus add our padding to the right and bottom of each face.
+ // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale
+ // accordingly.
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding);
}
-void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) {
+void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d, bool p_add_uv2, const float p_uv2_padding) {
int i, j, prevrow, thisrow, point;
float x, y, z;
float onethird = 1.0 / 3.0;
float twothirds = 2.0 / 3.0;
+ // Only used if we calculate UV2
+ // TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest)
+ float total_h = (size.x + size.z + (2.0 * p_uv2_padding));
+ float padding_h = p_uv2_padding / total_h;
+ float width_h = size.x / total_h;
+ float depth_h = size.z / total_h;
+ float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding));
+ float padding_v = p_uv2_padding / total_v;
+ float width_v = size.x / total_v;
+ float height_v = size.y / total_v;
+ float depth_v = size.z / total_v;
+
Vector3 start_pos = size * -0.5;
// set our bounding box
@@ -511,6 +664,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
point = 0;
@@ -525,18 +679,24 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
thisrow = point;
prevrow = 0;
for (j = 0; j <= subdivide_h + 1; j++) {
+ float v = j;
+ float v2 = v / (subdivide_w + 1.0);
+ v /= (2.0 * (subdivide_h + 1.0));
+
x = start_pos.x;
for (i = 0; i <= subdivide_w + 1; i++) {
float u = i;
- float v = j;
+ float u2 = u / (subdivide_w + 1.0);
u /= (3.0 * (subdivide_w + 1.0));
- v /= (2.0 * (subdivide_h + 1.0));
// front
points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z!
normals.push_back(Vector3(0.0, 0.0, 1.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(u, v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u2 * width_h, v2 * height_v));
+ }
point++;
// back
@@ -544,6 +704,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
normals.push_back(Vector3(0.0, 0.0, -1.0));
ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + u, v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v)));
+ }
point++;
if (i > 0 && j > 0) {
@@ -564,33 +727,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
indices.push_back(prevrow + i2 + 1);
indices.push_back(thisrow + i2 + 1);
indices.push_back(thisrow + i2 - 1);
- };
+ }
x += size.x / (subdivide_w + 1.0);
- };
+ }
y += size.y / (subdivide_h + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
// left + right
y = start_pos.y;
thisrow = point;
prevrow = 0;
for (j = 0; j <= (subdivide_h + 1); j++) {
+ float v = j;
+ float v2 = v / (subdivide_h + 1.0);
+ v /= (2.0 * (subdivide_h + 1.0));
+
z = start_pos.z;
for (i = 0; i <= (subdivide_d + 1); i++) {
float u = i;
- float v = j;
+ float u2 = u / (subdivide_d + 1.0);
u /= (3.0 * (subdivide_d + 1.0));
- v /= (2.0 * (subdivide_h + 1.0));
// right
points.push_back(Vector3(-start_pos.x, -y, -z));
normals.push_back(Vector3(1.0, 0.0, 0.0));
ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
uvs.push_back(Vector2(onethird + u, v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v));
+ }
point++;
// left
@@ -598,6 +767,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
normals.push_back(Vector3(-1.0, 0.0, 0.0));
ADD_TANGENT(0.0, 0.0, 1.0, 1.0);
uvs.push_back(Vector2(u, 0.5 + v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v)));
+ }
point++;
if (i > 0 && j > 0) {
@@ -618,33 +790,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
indices.push_back(prevrow + i2 + 1);
indices.push_back(thisrow + i2 + 1);
indices.push_back(thisrow + i2 - 1);
- };
+ }
z += size.z / (subdivide_d + 1.0);
- };
+ }
y += size.y / (subdivide_h + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
// top + bottom
z = start_pos.z;
thisrow = point;
prevrow = 0;
for (j = 0; j <= (subdivide_d + 1); j++) {
+ float v = j;
+ float v2 = v / (subdivide_d + 1.0);
+ v /= (2.0 * (subdivide_d + 1.0));
+
x = start_pos.x;
for (i = 0; i <= (subdivide_w + 1); i++) {
float u = i;
- float v = j;
+ float u2 = u / (subdivide_w + 1.0);
u /= (3.0 * (subdivide_w + 1.0));
- v /= (2.0 * (subdivide_d + 1.0));
// top
points.push_back(Vector3(-x, -start_pos.y, -z));
normals.push_back(Vector3(0.0, 1.0, 0.0));
ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(onethird + u, 0.5 + v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v)));
+ }
point++;
// bottom
@@ -652,6 +830,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
normals.push_back(Vector3(0.0, -1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + u, 0.5 + v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v)));
+ }
point++;
if (i > 0 && j > 0) {
@@ -672,20 +853,23 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int
indices.push_back(prevrow + i2 + 1);
indices.push_back(thisrow + i2 + 1);
indices.push_back(thisrow + i2 - 1);
- };
+ }
x += size.x / (subdivide_w + 1.0);
- };
+ }
z += size.z / (subdivide_d + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (p_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -708,6 +892,7 @@ void BoxMesh::_bind_methods() {
void BoxMesh::set_size(const Vector3 &p_size) {
size = p_size;
+ _update_lightmap_size();
_request_update();
}
@@ -748,18 +933,58 @@ BoxMesh::BoxMesh() {}
CylinderMesh
*/
+void CylinderMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ float top_circumference = top_radius * Math_PI * 2.0;
+ float bottom_circumference = bottom_radius * Math_PI * 2.0;
+
+ float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding;
+ _width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter.
+ _lightmap_size_hint.x = MAX(1.0, _width);
+
+ float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding);
+
+ _lightmap_size_hint.y = MAX(1.0, _height);
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void CylinderMesh::_create_mesh_array(Array &p_arr) const {
- create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom);
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding);
}
-void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom) {
+void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom, bool p_add_uv2, const float p_uv2_padding) {
int i, j, prevrow, thisrow, point;
- float x, y, z, u, v, radius;
+ float x, y, z, u, v, radius, radius_h;
+
+ // Only used if we calculate UV2
+ float top_circumference = top_radius * Math_PI * 2.0;
+ float bottom_circumference = bottom_radius * Math_PI * 2.0;
+ float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding);
+ float height_v = height / vertical_length;
+ float padding_v = p_uv2_padding / vertical_length;
+
+ float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding);
+ float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length;
+ float top_h = top_circumference / horizonal_length;
+ float bottom_h = bottom_circumference / horizonal_length;
+ float padding_h = p_uv2_padding / horizonal_length;
Vector<Vector3> points;
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
point = 0;
@@ -777,6 +1002,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
v /= (rings + 1);
radius = top_radius + ((bottom_radius - top_radius) * v);
+ radius_h = top_h + ((bottom_h - top_h) * v);
y = height * v;
y = (height * 0.5) - y;
@@ -793,6 +1019,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
normals.push_back(Vector3(x, side_normal_y, z).normalized());
ADD_TANGENT(z, 0.0, -x, 1.0)
uvs.push_back(Vector2(u, v * 0.5));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v));
+ }
point++;
if (i > 0 && j > 0) {
@@ -803,14 +1032,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
- };
+ }
+ }
prevrow = thisrow;
thisrow = point;
- };
+ }
- // add top
+ // Adjust for bottom section, only used if we calculate UV2s.
+ top_h = top_radius / horizonal_length;
+ float top_v = top_radius / vertical_length;
+ bottom_h = bottom_radius / horizonal_length;
+ float bottom_v = bottom_radius / vertical_length;
+
+ // Add top.
if (cap_top && top_radius > 0.0) {
y = height * 0.5;
@@ -819,6 +1054,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
normals.push_back(Vector3(0.0, 1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
uvs.push_back(Vector2(0.25, 0.75));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v)));
+ }
point++;
for (i = 0; i <= radial_segments; i++) {
@@ -836,17 +1074,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
normals.push_back(Vector3(0.0, 1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
uvs.push_back(Vector2(u, v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v)));
+ }
point++;
if (i > 0) {
indices.push_back(thisrow);
indices.push_back(point - 1);
indices.push_back(point - 2);
- };
- };
- };
+ }
+ }
+ }
- // add bottom
+ // Add bottom.
if (cap_bottom && bottom_radius > 0.0) {
y = height * -0.5;
@@ -855,6 +1096,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
normals.push_back(Vector3(0.0, -1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
uvs.push_back(Vector2(0.75, 0.75));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v)));
+ }
point++;
for (i = 0; i <= radial_segments; i++) {
@@ -872,20 +1116,26 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
normals.push_back(Vector3(0.0, -1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0)
uvs.push_back(Vector2(u, v));
+ if (p_add_uv2) {
+ uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v)));
+ }
point++;
if (i > 0) {
indices.push_back(thisrow);
indices.push_back(point - 2);
indices.push_back(point - 1);
- };
- };
- };
+ }
+ }
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (p_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -919,6 +1169,7 @@ void CylinderMesh::_bind_methods() {
void CylinderMesh::set_top_radius(const float p_radius) {
top_radius = p_radius;
+ _update_lightmap_size();
_request_update();
}
@@ -928,6 +1179,7 @@ float CylinderMesh::get_top_radius() const {
void CylinderMesh::set_bottom_radius(const float p_radius) {
bottom_radius = p_radius;
+ _update_lightmap_size();
_request_update();
}
@@ -937,6 +1189,7 @@ float CylinderMesh::get_bottom_radius() const {
void CylinderMesh::set_height(const float p_height) {
height = p_height;
+ _update_lightmap_size();
_request_update();
}
@@ -986,10 +1239,26 @@ CylinderMesh::CylinderMesh() {}
PlaneMesh
*/
+void PlaneMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding);
+ _lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding);
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void PlaneMesh::_create_mesh_array(Array &p_arr) const {
int i, j, prevrow, thisrow, point;
float x, z;
+ // Plane mesh can use default UV2 calculation as implemented in Primitive Mesh
+
Size2 start_pos = size * -0.5;
Vector3 normal = Vector3(0.0, 1.0, 0.0);
@@ -1043,15 +1312,15 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
+ }
x += size.x / (subdivide_w + 1.0);
- };
+ }
z += size.y / (subdivide_d + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
@@ -1088,6 +1357,7 @@ void PlaneMesh::_bind_methods() {
void PlaneMesh::set_size(const Size2 &p_size) {
size = p_size;
+ _update_lightmap_size();
_request_update();
}
@@ -1137,12 +1407,49 @@ PlaneMesh::PlaneMesh() {}
PrismMesh
*/
+void PrismMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ // left_to_right does not effect the surface area of the prism so we ignore that.
+ // TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent.
+
+ float width = (size.x + size.z) / texel_size;
+ float length = (size.y + size.y + size.z) / texel_size;
+
+ _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding;
+ _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding;
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void PrismMesh::_create_mesh_array(Array &p_arr) const {
int i, j, prevrow, thisrow, point;
float x, y, z;
float onethird = 1.0 / 3.0;
float twothirds = 2.0 / 3.0;
+ // Only used if we calculate UV2
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ float horizontal_total = size.x + size.z + 2.0 * _uv2_padding;
+ float width_h = size.x / horizontal_total;
+ float depth_h = size.z / horizontal_total;
+ float padding_h = _uv2_padding / horizontal_total;
+
+ float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding);
+ float height_v = size.y / vertical_total;
+ float depth_v = size.z / vertical_total;
+ float padding_v = _uv2_padding / vertical_total;
+
+ // and start building
+
Vector3 start_pos = size * -0.5;
// set our bounding box
@@ -1151,6 +1458,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
point = 0;
@@ -1171,12 +1479,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
float offset_front = (1.0 - scale) * onethird * left_to_right;
float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right);
+ float v = j;
+ float v2 = j / (subdivide_h + 1.0);
+ v /= (2.0 * (subdivide_h + 1.0));
+
x = 0.0;
for (i = 0; i <= (subdivide_w + 1); i++) {
float u = i;
- float v = j;
+ float u2 = i / (subdivide_w + 1.0);
u /= (3.0 * (subdivide_w + 1.0));
- v /= (2.0 * (subdivide_h + 1.0));
u *= scale;
@@ -1185,6 +1496,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
normals.push_back(Vector3(0.0, 0.0, 1.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(offset_front + u, v));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v));
+ }
point++;
/* back */
@@ -1192,6 +1506,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
normals.push_back(Vector3(0.0, 0.0, -1.0));
ADD_TANGENT(-1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + offset_back + u, v));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v));
+ }
point++;
if (i > 0 && j == 1) {
@@ -1224,15 +1541,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(prevrow + i2 + 1);
indices.push_back(thisrow + i2 + 1);
indices.push_back(thisrow + i2 - 1);
- };
+ }
x += scale * size.x / (subdivide_w + 1.0);
- };
+ }
y += size.y / (subdivide_h + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
/* left + right */
Vector3 normal_left, normal_right;
@@ -1246,6 +1563,10 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
thisrow = point;
prevrow = 0;
for (j = 0; j <= (subdivide_h + 1); j++) {
+ float v = j;
+ float v2 = j / (subdivide_h + 1.0);
+ v /= (2.0 * (subdivide_h + 1.0));
+
float left, right;
float scale = (y - start_pos.y) / size.y;
@@ -1255,15 +1576,17 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
z = start_pos.z;
for (i = 0; i <= (subdivide_d + 1); i++) {
float u = i;
- float v = j;
+ float u2 = u / (subdivide_d + 1.0);
u /= (3.0 * (subdivide_d + 1.0));
- v /= (2.0 * (subdivide_h + 1.0));
/* right */
points.push_back(Vector3(right, -y, -z));
normals.push_back(normal_right);
ADD_TANGENT(0.0, 0.0, -1.0, 1.0);
uvs.push_back(Vector2(onethird + u, v));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v));
+ }
point++;
/* left */
@@ -1271,6 +1594,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
normals.push_back(normal_left);
ADD_TANGENT(0.0, 0.0, 1.0, 1.0);
uvs.push_back(Vector2(u, 0.5 + v));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v));
+ }
point++;
if (i > 0 && j > 0) {
@@ -1291,33 +1617,39 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(prevrow + i2 + 1);
indices.push_back(thisrow + i2 + 1);
indices.push_back(thisrow + i2 - 1);
- };
+ }
z += size.z / (subdivide_d + 1.0);
- };
+ }
y += size.y / (subdivide_h + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
/* bottom */
z = start_pos.z;
thisrow = point;
prevrow = 0;
for (j = 0; j <= (subdivide_d + 1); j++) {
+ float v = j;
+ float v2 = v / (subdivide_d + 1.0);
+ v /= (2.0 * (subdivide_d + 1.0));
+
x = start_pos.x;
for (i = 0; i <= (subdivide_w + 1); i++) {
float u = i;
- float v = j;
+ float u2 = u / (subdivide_w + 1.0);
u /= (3.0 * (subdivide_w + 1.0));
- v /= (2.0 * (subdivide_d + 1.0));
/* bottom */
points.push_back(Vector3(x, start_pos.y, -z));
normals.push_back(Vector3(0.0, -1.0, 0.0));
ADD_TANGENT(1.0, 0.0, 0.0, 1.0);
uvs.push_back(Vector2(twothirds + u, 0.5 + v));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v));
+ }
point++;
if (i > 0 && j > 0) {
@@ -1328,20 +1660,23 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
+ }
x += size.x / (subdivide_w + 1.0);
- };
+ }
z += size.z / (subdivide_d + 1.0);
prevrow = thisrow;
thisrow = point;
- };
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -1377,6 +1712,7 @@ float PrismMesh::get_left_to_right() const {
void PrismMesh::set_size(const Vector3 &p_size) {
size = p_size;
+ _update_lightmap_size();
_request_update();
}
@@ -1417,22 +1753,50 @@ PrismMesh::PrismMesh() {}
SphereMesh
*/
+void SphereMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ float _width = radius * Math_TAU;
+ _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);
+ float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter..
+ _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void SphereMesh::_create_mesh_array(Array &p_arr) const {
- create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere);
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding);
}
-void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) {
+void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) {
int i, j, prevrow, thisrow, point;
float x, y, z;
float scale = height * (is_hemisphere ? 1.0 : 0.5);
+ // Only used if we calculate UV2
+ float circumference = radius * Math_TAU;
+ float horizontal_length = circumference + p_uv2_padding;
+ float center_h = 0.5 * circumference / horizontal_length;
+
+ float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding);
+
// set our bounding box
Vector<Vector3> points;
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
point = 0;
@@ -1467,9 +1831,13 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
points.push_back(p);
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)
uvs.push_back(Vector2(u, v));
+ if (p_add_uv2) {
+ float w_h = w * 2.0 * center_h;
+ uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v));
+ }
point++;
if (i > 0 && j > 0) {
@@ -1480,17 +1848,20 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int
indices.push_back(prevrow + i);
indices.push_back(thisrow + i);
indices.push_back(thisrow + i - 1);
- };
- };
+ }
+ }
prevrow = thisrow;
thisrow = point;
- };
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (p_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -1517,6 +1888,7 @@ void SphereMesh::_bind_methods() {
void SphereMesh::set_radius(const float p_radius) {
radius = p_radius;
+ _update_lightmap_size();
_request_update();
}
@@ -1526,6 +1898,7 @@ float SphereMesh::get_radius() const {
void SphereMesh::set_height(const float p_height) {
height = p_height;
+ _update_lightmap_size();
_request_update();
}
@@ -1553,6 +1926,7 @@ int SphereMesh::get_rings() const {
void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) {
is_hemisphere = p_is_hemisphere;
+ _update_lightmap_size();
_request_update();
}
@@ -1566,6 +1940,31 @@ SphereMesh::SphereMesh() {}
TorusMesh
*/
+void TorusMesh::_update_lightmap_size() {
+ if (get_add_uv2()) {
+ // size must have changed, update lightmap size hint
+ Size2i _lightmap_size_hint;
+ float texel_size = get_lightmap_texel_size();
+ float padding = get_uv2_padding();
+
+ float min_radius = inner_radius;
+ float max_radius = outer_radius;
+
+ if (min_radius > max_radius) {
+ SWAP(min_radius, max_radius);
+ }
+
+ float radius = (max_radius - min_radius) * 0.5;
+
+ float _width = max_radius * Math_TAU;
+ _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding);
+ float _height = radius * Math_TAU;
+ _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding);
+
+ set_lightmap_size_hint(_lightmap_size_hint);
+ }
+}
+
void TorusMesh::_create_mesh_array(Array &p_arr) const {
// set our bounding box
@@ -1573,6 +1972,7 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
Vector<Vector3> normals;
Vector<float> tangents;
Vector<Vector2> uvs;
+ Vector<Vector2> uv2s;
Vector<int> indices;
#define ADD_TANGENT(m_x, m_y, m_z, m_d) \
@@ -1592,6 +1992,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
float radius = (max_radius - min_radius) * 0.5;
+ // Only used if we calculate UV2
+ bool _add_uv2 = get_add_uv2();
+ float texel_size = get_lightmap_texel_size();
+ float _uv2_padding = get_uv2_padding() * texel_size;
+
+ float horizontal_total = max_radius * Math_TAU + _uv2_padding;
+ float max_h = max_radius * Math_TAU / horizontal_total;
+ float delta_h = (max_radius - min_radius) * Math_TAU / horizontal_total;
+
+ float height_v = radius * Math_TAU / (radius * Math_TAU + _uv2_padding);
+
for (int i = 0; i <= rings; i++) {
int prevrow = (i - 1) * (ring_segments + 1);
int thisrow = i * (ring_segments + 1);
@@ -1607,10 +2018,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj));
Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0);
+ float offset_h = 0.5 * (1.0 - normalj.x) * delta_h;
+ float adj_h = max_h - offset_h;
+ offset_h *= 0.5;
+
points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x));
normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x));
ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0);
uvs.push_back(Vector2(inci, incj));
+ if (_add_uv2) {
+ uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v));
+ }
if (i > 0 && j > 0) {
indices.push_back(thisrow + j - 1);
@@ -1628,6 +2046,9 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const {
p_arr[RS::ARRAY_NORMAL] = normals;
p_arr[RS::ARRAY_TANGENT] = tangents;
p_arr[RS::ARRAY_TEX_UV] = uvs;
+ if (_add_uv2) {
+ p_arr[RS::ARRAY_TEX_UV2] = uv2s;
+ }
p_arr[RS::ARRAY_INDEX] = indices;
}
@@ -1785,6 +2206,8 @@ Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const {
}
void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
+ // Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation
+
PackedVector3Array points;
PackedVector3Array normals;
PackedFloat32Array tangents;
@@ -1920,9 +2343,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(thisrow);
indices.push_back(point - 1);
indices.push_back(point - 2);
- };
- };
- };
+ }
+ }
+ }
float scale_neg = 1.0;
if (curve.is_valid() && curve->get_point_count() > 0) {
@@ -1983,9 +2406,9 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const {
indices.push_back(thisrow);
indices.push_back(point - 2);
indices.push_back(point - 1);
- };
- };
- };
+ }
+ }
+ }
p_arr[RS::ARRAY_VERTEX] = points;
p_arr[RS::ARRAY_NORMAL] = normals;
@@ -2109,6 +2532,8 @@ Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const {
}
void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const {
+ // Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation
+
PackedVector3Array points;
PackedVector3Array normals;
PackedFloat32Array tangents;
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index ee61f0ac55..06f9781b84 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -56,6 +56,9 @@ private:
Ref<Material> material;
bool flip_faces = false;
+ bool add_uv2 = false;
+ float uv2_padding = 2.0;
+
// make sure we do an update after we've finished constructing our object
mutable bool pending_request = true;
void _update() const;
@@ -70,6 +73,10 @@ protected:
void _request_update();
GDVIRTUAL0RC(Array, _create_mesh_array)
+ Vector2 get_uv2_scale(Vector2 p_margin_scale = Vector2(1.0, 1.0)) const;
+ float get_lightmap_texel_size() const;
+ virtual void _update_lightmap_size(){};
+
public:
virtual int get_surface_count() const override;
virtual int surface_get_array_len(int p_idx) const override;
@@ -98,6 +105,12 @@ public:
void set_flip_faces(bool p_enable);
bool get_flip_faces() const;
+ void set_add_uv2(bool p_enable);
+ bool get_add_uv2() const { return add_uv2; }
+
+ void set_uv2_padding(float p_padding);
+ float get_uv2_padding() const { return uv2_padding; }
+
PrimitiveMesh();
~PrimitiveMesh();
};
@@ -118,8 +131,10 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
- static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8);
+ static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
void set_radius(const float p_radius);
float get_radius() const;
@@ -152,8 +167,10 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
- static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0);
+ static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
void set_size(const Vector3 &p_size);
Vector3 get_size() const;
@@ -190,8 +207,10 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
- static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true);
+ static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
void set_top_radius(const float p_radius);
float get_top_radius() const;
@@ -241,6 +260,8 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
void set_size(const Size2 &p_size);
Size2 get_size() const;
@@ -292,6 +313,8 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
void set_left_to_right(const float p_left_to_right);
float get_left_to_right() const;
@@ -328,8 +351,10 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
- static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false);
+ static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0);
void set_radius(const float p_radius);
float get_radius() const;
@@ -365,6 +390,8 @@ protected:
static void _bind_methods();
virtual void _create_mesh_array(Array &p_arr) const override;
+ virtual void _update_lightmap_size() override;
+
public:
void set_inner_radius(const float p_inner_radius);
float get_inner_radius() const;
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 85b538b1d9..98a4909599 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -511,6 +511,7 @@ Error ResourceLoaderText::load() {
if (error) {
_printerr();
+ return error;
}
resource_current++;
@@ -884,6 +885,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d
error_text = "Unexpected end of file";
_printerr();
error = ERR_FILE_CORRUPT;
+ return;
}
}
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 4d566178a5..3a671edeea 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -177,7 +177,7 @@ bool Shader::is_text_shader() const {
}
bool Shader::has_parameter(const StringName &p_name) const {
- return params_cache.has("shader_parameter/" + p_name);
+ return params_cache.has(p_name);
}
void Shader::_update_shader() const {
diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp
index 61daf801e8..af84144591 100644
--- a/scene/resources/shape_2d.cpp
+++ b/scene/resources/shape_2d.cpp
@@ -116,7 +116,7 @@ bool Shape2D::is_collision_outline_enabled() {
return true;
}
#endif
- return GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true);
+ return GLOBAL_GET("debug/shapes/collision/draw_2d_outlines");
}
Shape2D::Shape2D(const RID &p_rid) {
diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h
index 4a875d039f..0ca6582965 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.h
+++ b/scene/resources/skeleton_modification_2d_fabrik.h
@@ -68,8 +68,8 @@ private:
float chain_tolarance = 0.01;
int chain_max_iterations = 10;
int chain_iterations = 0;
- Transform2D target_global_pose = Transform2D();
- Transform2D origin_global_pose = Transform2D();
+ Transform2D target_global_pose;
+ Transform2D origin_global_pose;
void fabrik_joint_update_bone2d_cache(int p_joint_idx);
void chain_backwards();
diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h
index e2e490d636..3e3aa5e587 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.h
+++ b/scene/resources/skeleton_modification_3d_fabrik.h
@@ -47,7 +47,7 @@ private:
bool auto_calculate_length = true;
bool use_tip_node = false;
- NodePath tip_node = NodePath();
+ NodePath tip_node;
ObjectID tip_node_cache;
bool use_target_basis = false;
@@ -68,8 +68,8 @@ private:
void update_joint_tip_cache(int p_joint_idx);
int final_joint_idx = 0;
- Transform3D target_global_pose = Transform3D();
- Transform3D origin_global_pose = Transform3D();
+ Transform3D target_global_pose;
+ Transform3D origin_global_pose;
void chain_backwards();
void chain_forwards();
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index f1eddd8ffc..cb5cb4ef96 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -336,7 +336,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
String word = str.substr(j, to - j);
- Color col = Color();
+ Color col;
if (keywords.has(word)) {
col = keywords[word];
} else if (member_keywords.has(word)) {
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 823d742d72..afab44834d 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -218,7 +218,48 @@ Array TextLine::get_objects() const {
}
Rect2 TextLine::get_object_rect(Variant p_key) const {
- return TS->shaped_text_get_object_rect(rid, p_key);
+ Vector2 ofs;
+
+ float length = TS->shaped_text_get_width(rid);
+ if (width > 0) {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT:
+ break;
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (length <= width) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((width - length) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(rid) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
+ }
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += width - length;
+ } else {
+ ofs.y += width - length;
+ }
+ } break;
+ }
+ }
+ if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.y += TS->shaped_text_get_ascent(rid);
+ } else {
+ ofs.x += TS->shaped_text_get_ascent(rid);
+ }
+
+ Rect2 rect = TS->shaped_text_get_object_rect(rid, p_key);
+ rect.position += ofs;
+
+ return rect;
}
void TextLine::set_horizontal_alignment(HorizontalAlignment p_alignment) {
@@ -276,11 +317,7 @@ float TextLine::get_width() const {
Size2 TextLine::get_size() const {
const_cast<TextLine *>(this)->_shape();
- if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y);
- } else {
- return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y);
- }
+ return TS->shaped_text_get_size(rid);
}
float TextLine::get_line_ascent() const {
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 7e9a2591e4..59bb24c8b8 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -540,16 +540,90 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
const_cast<TextParagraph *>(this)->_shape_lines();
ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2());
- Rect2 xrect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
- for (int i = 0; i < p_line; i++) {
- Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
+
+ Vector2 ofs;
+
+ float h_offset = 0.f;
+ if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
+ } else {
+ h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
+ }
+
+ for (int i = 0; i <= p_line; i++) {
+ float l_width = width;
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- xrect.position.y += lsize.y;
+ ofs.x = 0.f;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
} else {
- xrect.position.x += lsize.x;
+ ofs.y = 0.f;
+ ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
+ if (i <= dropcap_lines) {
+ if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
+ ofs.x -= h_offset;
+ }
+ l_width -= h_offset;
+ }
+ }
+ float length = TS->shaped_text_get_width(lines_rid[i]);
+ if (width > 0) {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ }
+ break;
+ case HORIZONTAL_ALIGNMENT_LEFT:
+ break;
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ if (length <= l_width) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += Math::floor((l_width - length) / 2.0);
+ } else {
+ ofs.y += Math::floor((l_width - length) / 2.0);
+ }
+ } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ }
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x += l_width - length;
+ } else {
+ ofs.y += l_width - length;
+ }
+ } break;
+ }
+ }
+ if (i != p_line) {
+ if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
+ ofs.x = 0.f;
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
+ } else {
+ ofs.y = 0.f;
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
+ }
}
}
- return xrect;
+
+ Rect2 rect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
+ rect.position += ofs;
+
+ return rect;
}
Size2 TextParagraph::get_line_size(int p_line) const {
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 67e23a14ca..b5754caa6a 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1664,7 +1664,7 @@ Ref<Image> AtlasTexture::get_image() const {
return Ref<Image>();
}
- return atlas->get_image()->get_rect(region);
+ return atlas->get_image()->get_region(region);
}
AtlasTexture::AtlasTexture() {}
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 3caf6484d9..d4ad81614d 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -3789,7 +3789,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
Size2i valid_area = txt->get_size() - margins;
// Compute the number of valid tiles in the tiles atlas
- Size2i grid_size = Size2i();
+ Size2i grid_size;
if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) {
valid_area -= texture_region_size;
grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 8f175e99a6..9f465a17e9 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -785,7 +785,7 @@ private:
bool flip_h = false;
bool flip_v = false;
bool transpose = false;
- Vector2i tex_offset = Vector2i();
+ Vector2i tex_offset;
Ref<Material> material = Ref<Material>();
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 04637983b5..03abac1b3e 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -3108,9 +3108,9 @@ void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_fun
return;
}
if (p_func == FUNC_PANNING) {
- set_input_port_default_value(2, Vector2()); // offset
+ set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // offset
} else { // FUNC_SCALING
- set_input_port_default_value(2, Vector2(0.5, 0.5)); // pivot
+ set_input_port_default_value(2, Vector2(0.5, 0.5), get_input_port_default_value(2)); // pivot
}
func = p_func;
emit_changed();
diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp
index d6e892cd93..1025d40b05 100644
--- a/scene/theme/theme_db.cpp
+++ b/scene/theme/theme_db.cpp
@@ -52,13 +52,13 @@ void ThemeDB::initialize_theme() {
ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)GLOBAL_DEF_RST("gui/theme/default_font_antialiasing", 1);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiasing", PropertyInfo(Variant::INT, "gui/theme/default_font_antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiasing", PropertyInfo(Variant::INT, "gui/theme/default_font_antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD Subpixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT);
ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO);
- ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
+ ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED));
const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false);
const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false);