summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/SCsub1
-rw-r--r--scene/2d/cpu_particles_2d.cpp2
-rw-r--r--scene/2d/gpu_particles_2d.cpp12
-rw-r--r--scene/2d/parallax_2d.cpp6
-rw-r--r--scene/2d/physics/SCsub1
-rw-r--r--scene/2d/physics/joints/SCsub1
-rw-r--r--scene/2d/physics/shape_cast_2d.cpp2
-rw-r--r--scene/2d/skeleton_2d.cpp44
-rw-r--r--scene/2d/skeleton_2d.h2
-rw-r--r--scene/2d/tile_map_layer.h2
-rw-r--r--scene/3d/SCsub1
-rw-r--r--scene/3d/audio_stream_player_3d.cpp11
-rw-r--r--scene/3d/audio_stream_player_3d.h1
-rw-r--r--scene/3d/cpu_particles_3d.cpp1
-rw-r--r--scene/3d/lightmap_gi.cpp13
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/3d/node_3d.cpp22
-rw-r--r--scene/3d/physics/SCsub1
-rw-r--r--scene/3d/physics/character_body_3d.cpp9
-rw-r--r--scene/3d/physics/joints/SCsub1
-rw-r--r--scene/3d/physics/vehicle_body_3d.cpp6
-rw-r--r--scene/3d/skeleton_3d.cpp223
-rw-r--r--scene/3d/skeleton_3d.h13
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/SCsub1
-rw-r--r--scene/animation/animation_blend_tree.cpp8
-rw-r--r--scene/animation/animation_mixer.cpp93
-rw-r--r--scene/animation/animation_mixer.h2
-rw-r--r--scene/animation/animation_player.cpp187
-rw-r--r--scene/animation/animation_player.h16
-rw-r--r--scene/audio/SCsub1
-rw-r--r--scene/debugger/SCsub1
-rw-r--r--scene/gui/SCsub1
-rw-r--r--scene/gui/base_button.cpp34
-rw-r--r--scene/gui/base_button.h2
-rw-r--r--scene/gui/button.cpp5
-rw-r--r--scene/gui/code_edit.cpp3
-rw-r--r--scene/gui/color_mode.h14
-rw-r--r--scene/gui/color_picker.cpp31
-rw-r--r--scene/gui/control.cpp17
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/file_dialog.cpp149
-rw-r--r--scene/gui/file_dialog.h16
-rw-r--r--scene/gui/flow_container.cpp5
-rw-r--r--scene/gui/flow_container.h3
-rw-r--r--scene/gui/graph_edit.cpp50
-rw-r--r--scene/gui/graph_node.cpp2
-rw-r--r--scene/gui/item_list.cpp121
-rw-r--r--scene/gui/item_list.h12
-rw-r--r--scene/gui/line_edit.cpp59
-rw-r--r--scene/gui/line_edit.h5
-rw-r--r--scene/gui/rich_text_label.cpp12
-rw-r--r--scene/gui/scroll_bar.cpp2
-rw-r--r--scene/gui/scroll_container.cpp34
-rw-r--r--scene/gui/scroll_container.h4
-rw-r--r--scene/gui/split_container.cpp244
-rw-r--r--scene/gui/split_container.h31
-rw-r--r--scene/gui/tab_bar.cpp16
-rw-r--r--scene/gui/tab_bar.h4
-rw-r--r--scene/gui/tab_container.cpp15
-rw-r--r--scene/gui/text_edit.cpp23
-rw-r--r--scene/gui/text_edit.h4
-rw-r--r--scene/gui/tree.cpp57
-rw-r--r--scene/gui/tree.h7
-rw-r--r--scene/main/SCsub1
-rw-r--r--scene/main/http_request.cpp3
-rw-r--r--scene/main/node.cpp52
-rw-r--r--scene/main/node.h18
-rw-r--r--scene/main/scene_tree.cpp16
-rw-r--r--scene/main/viewport.cpp30
-rw-r--r--scene/main/viewport.h1
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/2d/SCsub1
-rw-r--r--scene/resources/2d/tile_set.h38
-rw-r--r--scene/resources/3d/SCsub1
-rw-r--r--scene/resources/3d/fog_material.cpp2
-rw-r--r--scene/resources/3d/primitive_meshes.h6
-rw-r--r--scene/resources/3d/sky_material.cpp3
-rw-r--r--scene/resources/SCsub1
-rw-r--r--scene/resources/animation.cpp161
-rw-r--r--scene/resources/animation.h27
-rw-r--r--scene/resources/camera_attributes.h2
-rw-r--r--scene/resources/camera_texture.cpp24
-rw-r--r--scene/resources/camera_texture.h1
-rw-r--r--scene/resources/canvas_item_material.cpp2
-rw-r--r--scene/resources/external_texture.cpp89
-rw-r--r--scene/resources/external_texture.h66
-rw-r--r--scene/resources/gradient_texture.cpp12
-rw-r--r--scene/resources/gradient_texture.h12
-rw-r--r--scene/resources/material.cpp361
-rw-r--r--scene/resources/material.h27
-rw-r--r--scene/resources/mesh.cpp28
-rw-r--r--scene/resources/mesh.h28
-rw-r--r--scene/resources/particle_process_material.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp78
-rw-r--r--scene/resources/shader.cpp45
-rw-r--r--scene/resources/shader.h6
-rw-r--r--scene/resources/style_box.cpp2
-rw-r--r--scene/resources/style_box.h2
-rw-r--r--scene/resources/style_box_flat.cpp147
-rw-r--r--scene/resources/syntax_highlighter.cpp2
-rw-r--r--scene/resources/texture.cpp30
-rw-r--r--scene/resources/texture.h30
-rw-r--r--scene/resources/video_stream.cpp2
-rw-r--r--scene/resources/video_stream.h2
-rw-r--r--scene/resources/visual_shader_nodes.cpp177
-rw-r--r--scene/resources/visual_shader_nodes.h35
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
-rw-r--r--scene/theme/SCsub1
-rw-r--r--scene/theme/default_theme.cpp4
-rw-r--r--scene/theme/icons/SCsub1
-rw-r--r--scene/theme/icons/toggle_filename_filter.svg1
113 files changed, 2430 insertions, 831 deletions
diff --git a/scene/2d/SCsub b/scene/2d/SCsub
index 94e1ab6c96..6f6bf9818c 100644
--- a/scene/2d/SCsub
+++ b/scene/2d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 9c9ba93b41..754afb0527 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1288,7 +1288,7 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,suffix:s"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,suffix:s"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index bfbdb49f22..cfdcbee86a 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -821,19 +821,17 @@ void GPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_amount_ratio", "get_amount_ratio");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles2D"), "set_sub_emitter", "get_sub_emitter");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticleProcessMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,suffix:s"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.001"), "set_interp_to_end", "get_interp_to_end");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,suffix:s"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.001"), "set_interp_to_end", "get_interp_to_end");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
ADD_GROUP("Drawing", "");
@@ -845,7 +843,9 @@ void GPUParticles2D::_bind_methods() {
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", "");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticleProcessMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME);
diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp
index fdb2d2cdd0..c6176390dc 100644
--- a/scene/2d/parallax_2d.cpp
+++ b/scene/2d/parallax_2d.cpp
@@ -83,7 +83,11 @@ void Parallax2D::_validate_property(PropertyInfo &p_property) const {
void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) {
if (!ignore_camera_scroll) {
if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
- set_screen_offset((p_adj_screen_pos + Vector2(0.5, 0.5)).floor());
+ Size2 vps = get_viewport_rect().size;
+ Vector2 offset;
+ offset.x = ((int)vps.width % 2) ? 0.0 : 0.5;
+ offset.y = ((int)vps.height % 2) ? 0.0 : 0.5;
+ set_screen_offset((p_adj_screen_pos + offset).floor());
} else {
set_screen_offset(p_adj_screen_pos);
}
diff --git a/scene/2d/physics/SCsub b/scene/2d/physics/SCsub
index e7fd3fe643..5f9747514a 100644
--- a/scene/2d/physics/SCsub
+++ b/scene/2d/physics/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/2d/physics/joints/SCsub b/scene/2d/physics/joints/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/2d/physics/joints/SCsub
+++ b/scene/2d/physics/joints/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/2d/physics/shape_cast_2d.cpp b/scene/2d/physics/shape_cast_2d.cpp
index b92978bcad..dd9d589165 100644
--- a/scene/2d/physics/shape_cast_2d.cpp
+++ b/scene/2d/physics/shape_cast_2d.cpp
@@ -34,7 +34,7 @@
#include "scene/2d/physics/collision_object_2d.h"
#include "scene/2d/physics/physics_body_2d.h"
#include "scene/resources/2d/circle_shape_2d.h"
-#include "servers/physics_2d/godot_physics_server_2d.h"
+#include "servers/physics_server_2d.h"
void ShapeCast2D::set_target_position(const Vector2 &p_point) {
target_position = p_point;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 90bfb4c84c..39cfccf983 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -159,7 +159,7 @@ void Bone2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (skeleton) {
- for (int i = 0; i < skeleton->bones.size(); i++) {
+ for (uint32_t i = 0; i < skeleton->bones.size(); i++) {
if (skeleton->bones[i].bone == this) {
skeleton->bones.remove_at(i);
break;
@@ -555,17 +555,17 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorting so that they are always in the same order/index
- for (int i = 0; i < bones.size(); i++) {
- bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
- bones.write[i].bone->skeleton_index = i;
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index = i;
Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
if (parent_bone) {
- bones.write[i].parent_index = parent_bone->skeleton_index;
+ bones[i].parent_index = parent_bone->skeleton_index;
} else {
- bones.write[i].parent_index = -1;
+ bones[i].parent_index = -1;
}
- bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest();
+ bones[i].local_pose_override = bones[i].bone->get_skeleton_rest();
}
transform_dirty = true;
@@ -594,16 +594,16 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- for (int i = 0; i < bones.size(); i++) {
- ERR_CONTINUE(bones[i].parent_index >= i);
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ ERR_CONTINUE(bones[i].parent_index >= (int)i);
if (bones[i].parent_index >= 0) {
- bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
} else {
- bones.write[i].accum_transform = bones[i].bone->get_transform();
+ bones[i].accum_transform = bones[i].bone->get_transform();
}
}
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
RS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
@@ -621,7 +621,7 @@ int Skeleton2D::get_bone_count() const {
Bone2D *Skeleton2D::get_bone(int p_idx) {
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- ERR_FAIL_INDEX_V(p_idx, bones.size(), nullptr);
+ ERR_FAIL_INDEX_V(p_idx, (int)bones.size(), nullptr);
return bones[p_idx].bone;
}
@@ -733,14 +733,14 @@ RID Skeleton2D::get_skeleton() const {
}
void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent) {
- ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!");
- bones.write[p_bone_idx].local_pose_override = p_override;
- bones.write[p_bone_idx].local_pose_override_amount = p_amount;
- bones.write[p_bone_idx].local_pose_override_persistent = p_persistent;
+ ERR_FAIL_INDEX_MSG(p_bone_idx, (int)bones.size(), "Bone index is out of range!");
+ bones[p_bone_idx].local_pose_override = p_override;
+ bones[p_bone_idx].local_pose_override_amount = p_amount;
+ bones[p_bone_idx].local_pose_override_persistent = p_persistent;
}
Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) {
- ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!");
+ ERR_FAIL_INDEX_V_MSG(p_bone_idx, (int)bones.size(), Transform2D(), "Bone index is out of range!");
return bones[p_bone_idx].local_pose_override;
}
@@ -771,7 +771,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Do not cache the transform changes caused by the modifications!
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = false;
}
@@ -783,7 +783,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
// Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform.
if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (bones[i].local_pose_override_amount > 0) {
bones[i].bone->set_meta("_local_pose_override_enabled_", true);
@@ -793,7 +793,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
bones[i].bone->propagate_call("force_update_transform");
if (bones[i].local_pose_override_persistent) {
- bones.write[i].local_pose_override_amount = 0.0;
+ bones[i].local_pose_override_amount = 0.0;
}
} else {
// TODO: see if there is a way to undo the override without having to resort to setting every bone's transform.
@@ -804,7 +804,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
}
// Cache any future transform changes
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones[i].bone->copy_transform_to_cache = true;
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 033bdff41d..16bd6fd34b 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -123,7 +123,7 @@ class Skeleton2D : public Node2D {
bool local_pose_override_persistent = false;
};
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool bone_setup_dirty = true;
void _make_bone_setup_dirty();
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index cc0a5b49fb..6cb481849c 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -90,7 +90,7 @@ public:
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain); // For the center terrain bit
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
- TerrainConstraint(){};
+ TerrainConstraint() {}
};
#ifdef DEBUG_ENABLED
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
index 94e1ab6c96..6f6bf9818c 100644
--- a/scene/3d/SCsub
+++ b/scene/3d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 591528b915..98bee2115c 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -401,10 +401,19 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) {
total_max = MAX(total_max, listener_area_pos.length());
}
- if (total_max > max_distance) {
+ if (dist > total_max || total_max > max_distance) {
+ if (!was_further_than_max_distance_last_frame) {
+ HashMap<StringName, Vector<AudioFrame>> bus_volumes;
+ for (Ref<AudioStreamPlayback> &playback : internal->stream_playbacks) {
+ // So the player gets muted and mostly stops mixing when out of range.
+ AudioServer::get_singleton()->set_playback_bus_volumes_linear(playback, bus_volumes);
+ }
+ was_further_than_max_distance_last_frame = true; // Cache so we don't set the volume over and over.
+ }
continue; //can't hear this sound in this listener
}
}
+ was_further_than_max_distance_last_frame = false;
float multiplier = Math::db_to_linear(_get_attenuation_db(dist));
if (max_distance > 0) {
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index 72356faad7..91104a06c7 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -105,6 +105,7 @@ private:
float linear_attenuation = 0;
float max_distance = 0.0;
+ bool was_further_than_max_distance_last_frame = false;
Ref<VelocityTracker3D> velocity_tracker;
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index acbc443a93..5b84bf903f 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -1479,6 +1479,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh"), &CPUParticles3D::get_mesh);
ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart);
+ ClassDB::bind_method(D_METHOD("capture_aabb"), &CPUParticles3D::capture_aabb);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 26a574cd26..a1f32fccbe 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1602,8 +1602,17 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const {
PackedStringArray LightmapGI::get_configuration_warnings() const {
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
- if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
- warnings.push_back(RTR("Lightmap can only be baked from a device that supports the RD backends. Lightmap baking may fail."));
+#ifndef MODULE_LIGHTMAPPER_RD_ENABLED
+#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
+ warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
+#else
+ warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));
+#endif
+ return warnings;
+#endif
+
+ if (!DisplayServer::get_singleton()->can_create_rendering_device()) {
+ warnings.push_back(vformat(RTR("Lightmaps can only be baked from a GPU that supports the RenderingDevice backends.\nYour GPU (%s) does not support RenderingDevice, as it does not support Vulkan, Direct3D 12, or Metal.\nLightmap baking will not be available on this device, although rendering existing baked lightmaps will work."), RenderingServer::get_singleton()->get_video_adapter_name()));
return warnings;
}
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index f551cb401c..e8166802f8 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -332,8 +332,8 @@ void MeshInstance3D::create_multiple_convex_collisions(const Ref<MeshConvexDecom
void MeshInstance3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- _resolve_skeleton_path();
+ case NOTIFICATION_READY: {
+ callable_mp(this, &MeshInstance3D::_resolve_skeleton_path).call_deferred();
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
if (mesh.is_valid()) {
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 86ce8a881a..85de85a9a6 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -1181,15 +1181,16 @@ void Node3D::_validate_property(PropertyInfo &p_property) const {
}
bool Node3D::_property_can_revert(const StringName &p_name) const {
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
return true;
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
return true;
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
return true;
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
return true;
- } else if (p_name == "position") {
+ } else if (sname == "position") {
return true;
}
return false;
@@ -1198,35 +1199,36 @@ bool Node3D::_property_can_revert(const StringName &p_name) const {
bool Node3D::_property_get_revert(const StringName &p_name, Variant &r_property) const {
bool valid = false;
- if (p_name == "basis") {
+ const String sname = p_name;
+ if (sname == "basis") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis();
} else {
r_property = Basis();
}
- } else if (p_name == "scale") {
+ } else if (sname == "scale") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_scale();
} else {
r_property = Vector3(1.0, 1.0, 1.0);
}
- } else if (p_name == "quaternion") {
+ } else if (sname == "quaternion") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion());
} else {
r_property = Quaternion();
}
- } else if (p_name == "rotation") {
+ } else if (sname == "rotation") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
r_property = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order);
} else {
r_property = Vector3();
}
- } else if (p_name == "position") {
+ } else if (sname == "position") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid) {
r_property = Transform3D(variant).get_origin();
diff --git a/scene/3d/physics/SCsub b/scene/3d/physics/SCsub
index e7fd3fe643..5f9747514a 100644
--- a/scene/3d/physics/SCsub
+++ b/scene/3d/physics/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp
index dda3ea9cca..e3815e8219 100644
--- a/scene/3d/physics/character_body_3d.cpp
+++ b/scene/3d/physics/character_body_3d.cpp
@@ -60,8 +60,13 @@ bool CharacterBody3D::move_and_slide() {
// We need to check the platform_rid object still exists before accessing.
// A valid RID is no guarantee that the object has not been deleted.
- if (ObjectDB::get_instance(platform_object_id)) {
- //this approach makes sure there is less delay between the actual body velocity and the one we saved
+
+ // We can only perform the ObjectDB lifetime check on Object derived objects.
+ // Note that physics also creates RIDs for non-Object derived objects, these cannot
+ // be lifetime checked through ObjectDB, and therefore there is a still a vulnerability
+ // to dangling RIDs (access after free) in this scenario.
+ if (platform_object_id.is_null() || ObjectDB::get_instance(platform_object_id)) {
+ // This approach makes sure there is less delay between the actual body velocity and the one we saved.
bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid);
}
diff --git a/scene/3d/physics/joints/SCsub b/scene/3d/physics/joints/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/3d/physics/joints/SCsub
+++ b/scene/3d/physics/joints/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/3d/physics/vehicle_body_3d.cpp b/scene/3d/physics/vehicle_body_3d.cpp
index 5073705145..981e872af2 100644
--- a/scene/3d/physics/vehicle_body_3d.cpp
+++ b/scene/3d/physics/vehicle_body_3d.cpp
@@ -297,11 +297,11 @@ void VehicleWheel3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip");
ADD_GROUP("Suspension", "suspension_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_travel", "get_suspension_travel");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness", PROPERTY_HINT_NONE, U"suffix:N/mm"), "set_suspension_stiffness", "get_suspension_stiffness");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_suspension_max_force", "get_suspension_max_force");
ADD_GROUP("Damping", "damping_");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_compression", "get_damping_compression");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_relaxation", "get_damping_relaxation");
}
void VehicleWheel3D::set_engine_force(real_t p_engine_force) {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index db9c4db30d..9e4c9b1832 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -81,7 +81,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
if (which == bones.size() && what == "name") {
@@ -89,7 +89,7 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
return true;
}
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "parent") {
set_bone_parent(which, p_value);
@@ -153,10 +153,10 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
return false;
}
- int which = path.get_slicec('/', 1).to_int();
+ uint32_t which = path.get_slicec('/', 1).to_int();
String what = path.get_slicec('/', 2);
- ERR_FAIL_INDEX_V(which, bones.size(), false);
+ ERR_FAIL_UNSIGNED_INDEX_V(which, bones.size(), false);
if (what == "name") {
r_ret = get_bone_name(which);
@@ -182,7 +182,7 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
}
void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
const String prep = vformat("%s/%d/", PNAME("bones"), i);
p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
@@ -241,7 +241,7 @@ void Skeleton3D::_update_process_order() {
return;
}
- Bone *bonesptr = bones.ptrw();
+ Bone *bonesptr = bones.ptr();
int len = bones.size();
parentless_bones.clear();
@@ -276,6 +276,8 @@ void Skeleton3D::_update_process_order() {
concatenated_bone_names = StringName();
+ _update_bones_nested_set();
+
process_order_dirty = false;
emit_signal("bone_list_changed");
@@ -283,7 +285,7 @@ void Skeleton3D::_update_process_order() {
void Skeleton3D::_update_bone_names() const {
String names;
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
if (i > 0) {
names += ",";
}
@@ -331,16 +333,21 @@ void Skeleton3D::_notification(int p_what) {
updating = true;
- Bone *bonesptr = bones.ptrw();
+ Bone *bonesptr = bones.ptr();
int len = bones.size();
+ thread_local LocalVector<bool> bone_global_pose_dirty_backup;
+
// Process modifiers.
_find_modifiers();
if (!modifiers.is_empty()) {
// Store unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
bones_backup[i].save(bones[i]);
}
+ // Store dirty flags for global bone poses.
+ bone_global_pose_dirty_backup = bone_global_pose_dirty;
+
_process_modifiers();
}
@@ -412,9 +419,11 @@ void Skeleton3D::_notification(int p_what) {
if (!modifiers.is_empty()) {
// Restore unmodified bone poses.
- for (int i = 0; i < bones.size(); i++) {
- bones_backup[i].restore(bones.write[i]);
+ for (uint32_t i = 0; i < bones.size(); i++) {
+ bones_backup[i].restore(bones[i]);
}
+ // Restore dirty flags for global bone poses.
+ bone_global_pose_dirty = bone_global_pose_dirty_backup;
}
updating = false;
@@ -457,10 +466,111 @@ void Skeleton3D::_make_modifiers_dirty() {
_update_deferred(UPDATE_FLAG_MODIFIER);
}
+void Skeleton3D::_update_bones_nested_set() {
+ nested_set_offset_to_bone_index.resize(bones.size());
+ bone_global_pose_dirty.resize(bones.size());
+ _make_bone_global_poses_dirty();
+
+ int offset = 0;
+ for (int bone : parentless_bones) {
+ offset += _update_bone_nested_set(bone, offset);
+ }
+}
+
+int Skeleton3D::_update_bone_nested_set(int p_bone, int p_offset) {
+ Bone &bone = bones[p_bone];
+ int offset = p_offset + 1;
+ int span = 1;
+
+ for (int child_bone : bone.child_bones) {
+ int subspan = _update_bone_nested_set(child_bone, offset);
+ offset += subspan;
+ span += subspan;
+ }
+
+ nested_set_offset_to_bone_index[p_offset] = p_bone;
+ bone.nested_set_offset = p_offset;
+ bone.nested_set_span = span;
+
+ return span;
+}
+
+void Skeleton3D::_make_bone_global_poses_dirty() {
+ for (uint32_t i = 0; i < bone_global_pose_dirty.size(); i++) {
+ bone_global_pose_dirty[i] = true;
+ }
+}
+
+void Skeleton3D::_make_bone_global_pose_subtree_dirty(int p_bone) {
+ if (process_order_dirty) {
+ return;
+ }
+
+ const Bone &bone = bones[p_bone];
+ int span_offset = bone.nested_set_offset;
+ // No need to make subtree dirty when bone is already dirty.
+ if (bone_global_pose_dirty[span_offset]) {
+ return;
+ }
+
+ // Make global poses of subtree dirty.
+ int span_end = span_offset + bone.nested_set_span;
+ for (int i = span_offset; i < span_end; i++) {
+ bone_global_pose_dirty[i] = true;
+ }
+}
+
+void Skeleton3D::_update_bone_global_pose(int p_bone) {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX(p_bone, bone_size);
+
+ _update_process_order();
+
+ // Global pose is already calculated.
+ int nested_set_offset = bones[p_bone].nested_set_offset;
+ if (!bone_global_pose_dirty[nested_set_offset]) {
+ return;
+ }
+
+ thread_local LocalVector<int> bone_list;
+ bone_list.clear();
+ Transform3D global_pose;
+
+ // Create list of parent bones for which the global pose needs to be recalculated.
+ for (int bone = p_bone; bone >= 0; bone = bones[bone].parent) {
+ int offset = bones[bone].nested_set_offset;
+ // Stop searching when global pose is not dirty.
+ if (!bone_global_pose_dirty[offset]) {
+ global_pose = bones[bone].global_pose;
+ break;
+ }
+
+ bone_list.push_back(bone);
+ }
+
+ // Calculate global poses for all parent bones and the current bone.
+ for (int i = bone_list.size() - 1; i >= 0; i--) {
+ int bone_idx = bone_list[i];
+ Bone &bone = bones[bone_idx];
+ bool bone_enabled = bone.enabled && !show_rest_only;
+ Transform3D bone_pose = bone_enabled ? get_bone_pose(bone_idx) : get_bone_rest(bone_idx);
+
+ global_pose *= bone_pose;
+#ifndef DISABLE_DEPRECATED
+ if (bone.global_pose_override_amount >= CMP_EPSILON) {
+ global_pose = global_pose.interpolate_with(bone.global_pose_override, bone.global_pose_override_amount);
+ }
+#endif // _DISABLE_DEPRECATED
+
+ bone.global_pose = global_pose;
+ bone_global_pose_dirty[bone.nested_set_offset] = false;
+ }
+}
+
Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
+ const_cast<Skeleton3D *>(this)->_update_bone_global_pose(p_bone);
return bones[p_bone].global_pose;
}
@@ -534,7 +644,7 @@ void Skeleton3D::set_bone_name(int p_bone, const String &p_name) {
}
name_to_bone_index.erase(bones[p_bone].name);
- bones.write[p_bone].name = p_name;
+ bones[p_bone].name = p_name;
name_to_bone_index.insert(p_name, p_bone);
version++;
@@ -582,13 +692,13 @@ void Skeleton3D::set_bone_meta(int p_bone, const StringName &p_key, const Varian
ERR_FAIL_INDEX(p_bone, bone_size);
if (p_value.get_type() == Variant::NIL) {
- if (bones.write[p_bone].metadata.has(p_key)) {
- bones.write[p_bone].metadata.erase(p_key);
+ if (bones[p_bone].metadata.has(p_key)) {
+ bones[p_bone].metadata.erase(p_key);
}
return;
}
- bones.write[p_bone].metadata.insert(p_key, p_value, false);
+ bones[p_bone].metadata.insert(p_key, p_value, false);
}
bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
@@ -615,7 +725,7 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) {
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
ERR_FAIL_COND(p_bone == p_parent);
- bones.write[p_bone].parent = p_parent;
+ bones[p_bone].parent = p_parent;
process_order_dirty = true;
rest_dirty = true;
_make_dirty();
@@ -629,11 +739,11 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
int parent = bones[p_bone].parent;
while (parent >= 0) {
- bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
+ bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest;
parent = bones[parent].parent;
}
- bones.write[p_bone].parent = -1;
+ bones[p_bone].parent = -1;
process_order_dirty = true;
rest_dirty = true;
@@ -669,9 +779,10 @@ void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].rest = p_rest;
+ bones[p_bone].rest = p_rest;
rest_dirty = true;
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
Transform3D Skeleton3D::get_bone_rest(int p_bone) const {
const int bone_size = bones.size();
@@ -692,9 +803,10 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].enabled = p_enabled;
+ bones[p_bone].enabled = p_enabled;
emit_signal(SceneStringName(bone_enabled_changed), p_bone);
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
bool Skeleton3D::is_bone_enabled(int p_bone) const {
@@ -707,6 +819,7 @@ void Skeleton3D::set_show_rest_only(bool p_enabled) {
show_rest_only = p_enabled;
emit_signal(SceneStringName(show_rest_only_changed));
_make_dirty();
+ _make_bone_global_poses_dirty();
}
bool Skeleton3D::is_show_rest_only() const {
@@ -727,12 +840,13 @@ void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_pose.origin;
- bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
- bones.write[p_bone].pose_scale = p_pose.basis.get_scale();
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = p_pose.origin;
+ bones[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
+ bones[p_bone].pose_scale = p_pose.basis.get_scale();
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -740,30 +854,33 @@ void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_position = p_position;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_position = p_position;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_rotation = p_rotation;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_rotation = p_rotation;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose_scale = p_scale;
- bones.write[p_bone].pose_cache_dirty = true;
+ bones[p_bone].pose_scale = p_scale;
+ bones[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
}
@@ -794,7 +911,7 @@ void Skeleton3D::reset_bone_pose(int p_bone) {
}
void Skeleton3D::reset_bone_poses() {
- for (int i = 0; i < bones.size(); i++) {
+ for (uint32_t i = 0; i < bones.size(); i++) {
reset_bone_pose(i);
}
}
@@ -802,7 +919,7 @@ void Skeleton3D::reset_bone_poses() {
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
+ const_cast<Skeleton3D *>(this)->bones[p_bone].update_pose_cache();
return bones[p_bone].pose_cache;
}
@@ -853,7 +970,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
// Pose changed, rebuild cache of inverses.
const Bone *bonesptr = bones.ptr();
- int len = bones.size();
+ uint32_t len = bones.size();
// Calculate global rests and invert them.
LocalVector<int> bones_to_process;
@@ -877,7 +994,7 @@ Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
}
}
- for (int i = 0; i < len; i++) {
+ for (uint32_t i = 0; i < len; i++) {
// The inverse is what is actually required.
skin->set_bind_bone(i, i);
skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
@@ -937,15 +1054,15 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone_idx, bone_size);
- Bone *bonesptr = bones.ptrw();
- thread_local LocalVector<int> bones_to_process;
- bones_to_process.clear();
- bones_to_process.push_back(p_bone_idx);
+ Bone *bonesptr = bones.ptr();
- uint32_t index = 0;
- while (index < bones_to_process.size()) {
- int current_bone_idx = bones_to_process[index];
+ // Loop through nested set.
+ for (int offset = 0; offset < bone_size; offset++) {
+ if (!bone_global_pose_dirty[offset]) {
+ continue;
+ }
+ int current_bone_idx = nested_set_offset_to_bone_index[offset];
Bone &b = bonesptr[current_bone_idx];
bool bone_enabled = b.enabled && !show_rest_only;
@@ -992,13 +1109,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
#endif // _DISABLE_DEPRECATED
- // Add the bone's children to the list of bones to be processed.
- int child_bone_size = b.child_bones.size();
- for (int i = 0; i < child_bone_size; i++) {
- bones_to_process.push_back(b.child_bones[i]);
- }
-
- index++;
+ bone_global_pose_dirty[offset] = false;
}
}
@@ -1171,20 +1282,22 @@ void Skeleton3D::_bind_methods() {
#ifndef DISABLE_DEPRECATED
void Skeleton3D::clear_bones_global_pose_override() {
- for (int i = 0; i < bones.size(); i += 1) {
- bones.write[i].global_pose_override_amount = 0;
- bones.write[i].global_pose_override_reset = true;
+ for (uint32_t i = 0; i < bones.size(); i += 1) {
+ bones[i].global_pose_override_amount = 0;
+ bones[i].global_pose_override_reset = true;
}
_make_dirty();
+ _make_bone_global_poses_dirty();
}
void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].global_pose_override_amount = p_amount;
- bones.write[p_bone].global_pose_override = p_pose;
- bones.write[p_bone].global_pose_override_reset = !p_persistent;
+ bones[p_bone].global_pose_override_amount = p_amount;
+ bones[p_bone].global_pose_override = p_pose;
+ bones[p_bone].global_pose_override_reset = !p_persistent;
_make_dirty();
+ _make_bone_global_pose_subtree_dirty(p_bone);
}
Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 07bdeccf2f..0db12600c3 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -107,6 +107,8 @@ private:
Quaternion pose_rotation;
Vector3 pose_scale = Vector3(1, 1, 1);
Transform3D global_pose;
+ int nested_set_offset = 0; // Offset in nested set of bone hierarchy.
+ int nested_set_span = 0; // Subtree span in nested set of bone hierarchy.
void update_pose_cache() {
if (pose_cache_dirty) {
@@ -153,7 +155,7 @@ private:
HashSet<SkinReference *> skin_bindings;
void _skin_changed();
- Vector<Bone> bones;
+ LocalVector<Bone> bones;
bool process_order_dirty = false;
Vector<int> parentless_bones;
@@ -183,6 +185,15 @@ private:
void _make_modifiers_dirty();
LocalVector<BonePoseBackup> bones_backup;
+ // Global bone pose calculation.
+ LocalVector<int> nested_set_offset_to_bone_index; // Map from Bone::nested_set_offset to bone index.
+ LocalVector<bool> bone_global_pose_dirty; // Indexable with Bone::nested_set_offset.
+ void _update_bones_nested_set();
+ int _update_bone_nested_set(int p_bone, int p_offset);
+ void _make_bone_global_poses_dirty();
+ void _make_bone_global_pose_subtree_dirty(int p_bone);
+ void _update_bone_global_pose(int p_bone);
+
#ifndef DISABLE_DEPRECATED
void _add_bone_bind_compat_88791(const String &p_name);
diff --git a/scene/SCsub b/scene/SCsub
index b4b2d6dd0a..1eb4ffa53d 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/animation/SCsub b/scene/animation/SCsub
index d0aa0bc8aa..dd2b22c2e3 100644
--- a/scene/animation/SCsub
+++ b/scene/animation/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index cdc85d2b2d..a2aef60417 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -245,6 +245,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
if (!p_test_only) {
AnimationMixer::PlaybackInfo pi = p_playback_info;
+ pi.start = 0.0;
+ pi.end = cur_len;
if (play_mode == PLAY_MODE_FORWARD) {
pi.time = cur_playback_time;
pi.delta = cur_delta;
@@ -1139,7 +1141,11 @@ void AnimationNodeTransition::remove_input(int p_index) {
bool AnimationNodeTransition::set_input_name(int p_input, const String &p_name) {
pending_update = true;
- return AnimationNode::set_input_name(p_input, p_name);
+ if (!AnimationNode::set_input_name(p_input, p_name)) {
+ return false;
+ }
+ emit_signal(SNAME("tree_changed")); // For updating enum options.
+ return true;
}
void AnimationNodeTransition::set_input_as_auto_advance(int p_input, bool p_enable) {
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 664302d45e..eb8bc8c382 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -1117,6 +1117,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
Ref<Animation> a = ai.animation_data.animation;
double time = ai.playback_info.time;
double delta = ai.playback_info.delta;
+ double start = ai.playback_info.start;
+ double end = ai.playback_info.end;
bool seeked = ai.playback_info.seeked;
Animation::LoopedFlag looped_flag = ai.playback_info.looped_flag;
bool is_external_seeking = ai.playback_info.is_external_seeking;
@@ -1168,32 +1170,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1208,10 +1210,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
- a->try_position_track_interpolate(i, (double)a_length, &loc[1]);
+ a->try_position_track_interpolate(i, end, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1220,10 +1222,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
- a->try_position_track_interpolate(i, 0, &loc[1]);
+ a->try_position_track_interpolate(i, start, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
@@ -1234,7 +1236,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_position_track_interpolate(i, time, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Vector3 loc;
@@ -1256,32 +1258,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1296,10 +1298,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
- a->try_rotation_track_interpolate(i, (double)a_length, &rot[1]);
+ a->try_rotation_track_interpolate(i, end, &rot[1]);
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1308,9 +1310,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
- a->try_rotation_track_interpolate(i, 0, &rot[1]);
+ a->try_rotation_track_interpolate(i, start, &rot[1]);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
@@ -1321,7 +1323,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_rotation_track_interpolate(i, time, &rot[1]);
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Quaternion rot;
@@ -1343,32 +1345,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1383,10 +1385,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
- a->try_scale_track_interpolate(i, (double)a_length, &scale[1]);
+ a->try_scale_track_interpolate(i, end, &scale[1]);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1395,10 +1397,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
- a->try_scale_track_interpolate(i, 0, &scale[1]);
+ a->try_scale_track_interpolate(i, start, &scale[1]);
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
@@ -1409,7 +1411,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_scale_track_interpolate(i, time, &scale[1]);
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Vector3 scale;
@@ -1671,6 +1673,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (!player2) {
continue;
}
+ // TODO: Make it possible to embed section info in animation track keys.
if (seeked) {
// Seek.
int idx = a->track_find_key(i, time, Animation::FIND_MODE_NEAREST, true);
@@ -1683,19 +1686,19 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
Ref<Animation> anim = player2->get_animation(anim_name);
- double at_anim_pos = 0.0;
+ double at_anim_pos = start;
switch (anim->get_loop_mode()) {
case Animation::LOOP_NONE: {
- if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + (double)anim->get_length())) || (backward && Animation::is_less_or_equal_approx(time, pos)))) {
+ if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + end)) || (backward && Animation::is_less_or_equal_approx(time, pos + start)))) {
continue; // Do nothing if current time is outside of length when started.
}
- at_anim_pos = MIN((double)anim->get_length(), time - pos); // Seek to end.
+ at_anim_pos = MIN(end, time - pos); // Seek to end.
} break;
case Animation::LOOP_LINEAR: {
- at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); // Seek to loop.
+ at_anim_pos = Math::fposmod(time - pos - start, end - start) + start; // Seek to loop.
} break;
case Animation::LOOP_PINGPONG: {
- at_anim_pos = Math::pingpong(time - pos, (double)a_length);
+ at_anim_pos = Math::pingpong(time - pos - start, end - start) + start;
} break;
default:
break;
@@ -2092,6 +2095,8 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() {
PlaybackInfo pi;
pi.time = 0;
pi.delta = 0;
+ pi.start = 0;
+ pi.end = reset_anim->get_length();
pi.seeked = true;
pi.weight = 1.0;
make_animation_instance(SceneStringName(RESET), pi);
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 5482197fbd..27c9a00a9c 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -85,6 +85,8 @@ public:
struct PlaybackInfo {
double time = 0.0;
double delta = 0.0;
+ double start = 0.0;
+ double end = 0.0;
bool seeked = false;
bool is_external_seeking = false;
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index a4aa383a9d..8a2ca47920 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -164,39 +164,41 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
double delta = p_started ? 0 : p_delta * speed;
double next_pos = cd.pos + delta;
- double len = cd.from->animation->get_length();
+ double start = get_section_start_time();
+ double end = get_section_end_time();
+
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
switch (cd.from->animation->get_loop_mode()) {
case Animation::LOOP_NONE: {
- if (Animation::is_less_approx(next_pos, 0)) {
- next_pos = 0;
- } else if (Animation::is_greater_approx(next_pos, len)) {
- next_pos = len;
+ if (Animation::is_less_approx(next_pos, start)) {
+ next_pos = start;
+ } else if (Animation::is_greater_approx(next_pos, end)) {
+ next_pos = end;
}
delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here).
} break;
case Animation::LOOP_LINEAR: {
- if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
+ if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
looped_flag = Animation::LOOPED_FLAG_START;
}
- if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
+ if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
looped_flag = Animation::LOOPED_FLAG_END;
}
- next_pos = Math::fposmod(next_pos, (double)len);
+ next_pos = Math::fposmod(next_pos - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
+ if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
cd.speed_scale *= -1.0;
looped_flag = Animation::LOOPED_FLAG_START;
}
- if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
+ if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
cd.speed_scale *= -1.0;
looped_flag = Animation::LOOPED_FLAG_END;
}
- next_pos = Math::pingpong(next_pos, (double)len);
+ next_pos = Math::pingpong(next_pos - start, end - start) + start;
} break;
default:
@@ -208,18 +210,18 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
// End detection.
if (p_is_current) {
if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) {
- if (!backwards && Animation::is_less_or_equal_approx(prev_pos, len) && Math::is_equal_approx(next_pos, len)) {
+ if (!backwards && Animation::is_less_or_equal_approx(prev_pos, end) && Math::is_equal_approx(next_pos, end)) {
// Playback finished.
- next_pos = len; // Snap to the edge.
+ next_pos = end; // Snap to the edge.
end_reached = true;
- end_notify = Animation::is_less_approx(prev_pos, len); // Notify only if not already at the end.
+ end_notify = Animation::is_less_approx(prev_pos, end); // Notify only if not already at the end.
p_blend = 1.0;
}
- if (backwards && Animation::is_greater_or_equal_approx(prev_pos, 0) && Math::is_equal_approx(next_pos, 0)) {
+ if (backwards && Animation::is_greater_or_equal_approx(prev_pos, start) && Math::is_equal_approx(next_pos, start)) {
// Playback finished.
- next_pos = 0; // Snap to the edge.
+ next_pos = start; // Snap to the edge.
end_reached = true;
- end_notify = Animation::is_greater_approx(prev_pos, 0); // Notify only if not already at the beginning.
+ end_notify = Animation::is_greater_approx(prev_pos, start); // Notify only if not already at the beginning.
p_blend = 1.0;
}
}
@@ -231,10 +233,14 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
if (p_started) {
pi.time = prev_pos;
pi.delta = 0;
+ pi.start = start;
+ pi.end = end;
pi.seeked = true;
} else {
pi.time = next_pos;
pi.delta = delta;
+ pi.start = start;
+ pi.end = end;
pi.seeked = p_seeked;
}
if (Math::is_zero_approx(pi.delta) && backwards) {
@@ -378,6 +384,14 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
play(p_name, p_custom_blend, -1, true);
}
+void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend) {
+ play_section_with_markers(p_name, p_start_marker, p_end_marker, p_custom_blend, -1, true);
+}
+
+void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) {
+ play_section(p_name, p_start_time, p_end_time, -1, true);
+}
+
void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
if (auto_capture) {
play_with_capture(p_name, auto_capture_duration, p_custom_blend, p_custom_scale, p_from_end, auto_capture_transition_type, auto_capture_ease_type);
@@ -387,6 +401,10 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
}
void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+ play_section_with_markers(p_name, StringName(), StringName(), p_custom_blend, p_custom_scale, p_from_end);
+}
+
+void AnimationPlayer::play_section_with_markers(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend, float p_custom_scale, bool p_from_end) {
StringName name = p_name;
if (name == StringName()) {
@@ -395,6 +413,38 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
+ Ref<Animation> animation = animation_set[name].animation;
+
+ ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
+ ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name));
+ ERR_FAIL_COND_MSG(p_end_marker && !animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, name));
+
+ double start_time = p_start_marker ? animation->get_marker_time(p_start_marker) : -1;
+ double end_time = p_end_marker ? animation->get_marker_time(p_end_marker) : -1;
+
+ ERR_FAIL_COND_MSG(p_start_marker && p_end_marker && Animation::is_greater_approx(start_time, end_time), vformat("End marker %s is placed earlier than start marker %s in animation: %s.", p_end_marker, p_start_marker, name));
+
+ if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
+ WARN_PRINT_ED(vformat("Negative time start marker: %s is invalid in the section, so the start of the animation: %s is used instead.", p_start_marker, playback.current.from->animation->get_name()));
+ }
+ if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
+ WARN_PRINT_ED(vformat("Negative time end marker: %s is invalid in the section, so the end of the animation: %s is used instead.", p_end_marker, playback.current.from->animation->get_name()));
+ }
+
+ play_section(name, start_time, end_time, p_custom_blend, p_custom_scale, p_from_end);
+}
+
+void AnimationPlayer::play_section(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+ StringName name = p_name;
+
+ if (name == StringName()) {
+ name = playback.assigned;
+ }
+
+ ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
+ ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Math::is_equal_approx(p_start_time, p_end_time), "Start time and end time must not equal to each other.");
+ ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Animation::is_greater_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
+
Playback &c = playback;
if (c.current.from) {
@@ -442,22 +492,27 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
c.current.from = &animation_set[name];
c.current.speed_scale = p_custom_scale;
+ c.current.start_time = p_start_time;
+ c.current.end_time = p_end_time;
+
+ double start = get_section_start_time();
+ double end = get_section_end_time();
if (!end_reached) {
playback_queue.clear();
}
if (c.assigned != name) { // Reset.
- c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0;
+ c.current.pos = p_from_end ? end : start;
c.assigned = name;
emit_signal(SNAME("current_animation_changed"), c.assigned);
} else {
- if (p_from_end && Math::is_zero_approx(c.current.pos)) {
+ if (p_from_end && Math::is_equal_approx(c.current.pos, start)) {
// Animation reset but played backwards, set position to the end.
- seek_internal(c.current.from->animation->get_length(), true, true, true);
- } else if (!p_from_end && Math::is_equal_approx(c.current.pos, (double)c.current.from->animation->get_length())) {
+ seek_internal(end, true, true, true);
+ } else if (!p_from_end && Math::is_equal_approx(c.current.pos, end)) {
// Animation resumed but already ended, set position to the beginning.
- seek_internal(0, true, true, true);
+ seek_internal(start, true, true, true);
} else if (playing) {
return;
}
@@ -551,6 +606,8 @@ void AnimationPlayer::set_assigned_animation(const String &p_animation) {
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
playback.current.pos = 0;
playback.current.from = &animation_set[p_animation];
+ playback.current.start_time = -1;
+ playback.current.end_time = -1;
playback.assigned = p_animation;
emit_signal(SNAME("current_animation_changed"), playback.assigned);
}
@@ -603,6 +660,12 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_
}
}
+ double start = get_section_start_time();
+ double end = get_section_end_time();
+
+ // Clamp the seek position.
+ p_time = CLAMP(p_time, start, end);
+
playback.seeked = true;
playback.internal_seeked = p_is_internal_seek;
@@ -641,6 +704,55 @@ double AnimationPlayer::get_current_animation_length() const {
return playback.current.from->animation->get_length();
}
+void AnimationPlayer::set_section_with_markers(const StringName &p_start_marker, const StringName &p_end_marker) {
+ ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
+ ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
+ ERR_FAIL_COND_MSG(p_start_marker && !playback.current.from->animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
+ ERR_FAIL_COND_MSG(p_end_marker && !playback.current.from->animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
+ double start_time = p_start_marker ? playback.current.from->animation->get_marker_time(p_start_marker) : -1;
+ double end_time = p_end_marker ? playback.current.from->animation->get_marker_time(p_end_marker) : -1;
+ if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
+ WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
+ }
+ if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
+ WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
+ }
+ set_section(start_time, end_time);
+}
+
+void AnimationPlayer::set_section(double p_start_time, double p_end_time) {
+ ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
+ ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
+ playback.current.start_time = p_start_time;
+ playback.current.end_time = p_end_time;
+ playback.current.pos = CLAMP(playback.current.pos, get_section_start_time(), get_section_end_time());
+}
+
+void AnimationPlayer::reset_section() {
+ playback.current.start_time = -1;
+ playback.current.end_time = -1;
+}
+
+double AnimationPlayer::get_section_start_time() const {
+ ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation.");
+ if (Animation::is_less_approx(playback.current.start_time, 0) || playback.current.start_time > playback.current.from->animation->get_length()) {
+ return 0;
+ }
+ return playback.current.start_time;
+}
+
+double AnimationPlayer::get_section_end_time() const {
+ ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation.");
+ if (Animation::is_less_approx(playback.current.end_time, 0) || playback.current.end_time > playback.current.from->animation->get_length()) {
+ return playback.current.from->animation->get_length();
+ }
+ return playback.current.end_time;
+}
+
+bool AnimationPlayer::has_section() const {
+ return Animation::is_greater_or_equal_approx(playback.current.start_time, 0) || Animation::is_greater_or_equal_approx(playback.current.end_time, 0);
+}
+
void AnimationPlayer::set_autoplay(const String &p_name) {
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect.");
@@ -665,13 +777,14 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
_clear_caches();
Playback &c = playback;
// c.blend.clear();
+ double start = c.current.from ? get_section_start_time() : 0;
if (p_reset) {
c.blend.clear();
if (p_keep_state) {
- c.current.pos = 0;
+ c.current.pos = start;
} else {
is_stopping = true;
- seek_internal(0, true, true, true);
+ seek_internal(start, true, true, true);
is_stopping = false;
}
c.current.from = nullptr;
@@ -763,20 +876,6 @@ Tween::EaseType AnimationPlayer::get_auto_capture_ease_type() const {
return auto_capture_ease_type;
}
-#ifdef TOOLS_ENABLED
-void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- const String pf = p_function;
- if (p_idx == 0 && (pf == "play" || pf == "play_backwards" || pf == "has_animation" || pf == "queue")) {
- List<StringName> al;
- get_animation_list(&al);
- for (const StringName &name : al) {
- r_options->push_back(String(name).quote());
- }
- }
- AnimationMixer::get_argument_options(p_function, p_idx, r_options);
-}
-#endif
-
void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
AnimationMixer::_animation_removed(p_name, p_library);
@@ -863,7 +962,11 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auto_capture_ease_type"), &AnimationPlayer::get_auto_capture_ease_type);
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("play_section_with_markers", "name", "start_marker", "end_marker", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("play_section", "name", "start_time", "end_time", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("play_section_with_markers_backwards", "name", "start_marker", "end_marker", "custom_blend"), &AnimationPlayer::play_section_with_markers_backwards, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("play_section_backwards", "name", "start_time", "end_time", "custom_blend"), &AnimationPlayer::play_section_backwards, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
@@ -893,6 +996,14 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
+ ClassDB::bind_method(D_METHOD("set_section_with_markers", "start_marker", "end_marker"), &AnimationPlayer::set_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("set_section", "start_time", "end_time"), &AnimationPlayer::set_section, DEFVAL(-1), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("reset_section"), &AnimationPlayer::reset_section);
+
+ ClassDB::bind_method(D_METHOD("get_section_start_time"), &AnimationPlayer::get_section_start_time);
+ ClassDB::bind_method(D_METHOD("get_section_end_time"), &AnimationPlayer::get_section_end_time);
+ ClassDB::bind_method(D_METHOD("has_section"), &AnimationPlayer::has_section);
+
ClassDB::bind_method(D_METHOD("seek", "seconds", "update", "update_only"), &AnimationPlayer::seek, DEFVAL(false), DEFVAL(false));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index e05a2c9935..3223e2522d 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -68,6 +68,8 @@ private:
AnimationData *from = nullptr;
double pos = 0.0;
float speed_scale = 1.0;
+ double start_time = 0.0;
+ double end_time = 0.0;
};
struct Blend {
@@ -177,7 +179,11 @@ public:
Tween::EaseType get_auto_capture_ease_type() const;
void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+ void play_section_with_markers(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+ void play_section(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
+ void play_section_with_markers_backwards(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1);
+ void play_section_backwards(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1);
void play_with_capture(const StringName &p_name = StringName(), double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
void queue(const StringName &p_name);
Vector<String> get_queue();
@@ -207,9 +213,13 @@ public:
double get_current_animation_position() const;
double get_current_animation_length() const;
-#ifdef TOOLS_ENABLED
- void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif
+ void set_section_with_markers(const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName());
+ void set_section(double p_start_time = -1, double p_end_time = -1);
+ void reset_section();
+
+ double get_section_start_time() const;
+ double get_section_end_time() const;
+ bool has_section() const;
virtual void advance(double p_time) override;
diff --git a/scene/audio/SCsub b/scene/audio/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/audio/SCsub
+++ b/scene/audio/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/debugger/SCsub b/scene/debugger/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/debugger/SCsub
+++ b/scene/debugger/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/gui/SCsub b/scene/gui/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/gui/SCsub
+++ b/scene/gui/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 72299f788d..34f5095493 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
+#include "scene/gui/label.h"
#include "scene/main/window.h"
void BaseButton::_unpress_group() {
@@ -390,16 +391,31 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
}
}
-String BaseButton::get_tooltip(const Point2 &p_pos) const {
- String tooltip = Control::get_tooltip(p_pos);
- if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
- String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
- text += "\n" + atr(tooltip);
- }
- tooltip = text;
+Control *BaseButton::make_custom_tooltip(const String &p_text) const {
+ Control *control = Control::make_custom_tooltip(p_text);
+ if (control) {
+ return control;
+ }
+ if (!shortcut_in_tooltip || shortcut.is_null() || !shortcut->has_valid_event()) {
+ return nullptr; // Use the default tooltip label.
}
- return tooltip;
+
+ String text = atr(shortcut->get_name()) + " (" + shortcut->get_as_text() + ")";
+ if (!p_text.is_empty() && shortcut->get_name().nocasecmp_to(p_text) != 0) {
+ text += "\n" + atr(p_text);
+ }
+
+ // Make a label similar to the default tooltip label.
+ // Auto translation is disabled because we already did that manually above.
+ //
+ // We can't customize the tooltip text by overriding `get_tooltip()`
+ // because otherwise user-defined `_make_custom_tooltip()` would receive
+ // the translated and annotated text.
+ Label *label = memnew(Label(text));
+ label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ label->set_theme_type_variation(SNAME("TooltipLabel"));
+
+ return label;
}
void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index a8d5cee44c..8405acb21d 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -134,7 +134,7 @@ public:
void set_shortcut(const Ref<Shortcut> &p_shortcut);
Ref<Shortcut> get_shortcut() const;
- virtual String get_tooltip(const Point2 &p_pos) const override;
+ virtual Control *make_custom_tooltip(const String &p_text) const override;
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index dd344121e1..0a5f2ec6c7 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -30,9 +30,7 @@
#include "button.h"
-#include "core/string/translation.h"
#include "scene/theme/theme_db.h"
-#include "servers/rendering_server.h"
Size2 Button::get_minimum_size() const {
Ref<Texture2D> _icon = icon;
@@ -437,6 +435,9 @@ void Button::_notification(int p_what) {
text_buf->set_alignment(align_rtl_checked);
float text_buf_width = Math::ceil(MAX(1.0f, drawable_size_remained.width)); // The space's width filled by the text_buf.
+ if (autowrap_mode != TextServer::AUTOWRAP_OFF && !Math::is_equal_approx(text_buf_width, text_buf->get_width())) {
+ update_minimum_size();
+ }
text_buf->set_width(text_buf_width);
Point2 text_ofs;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index dff1cf34c2..7346c9dcd3 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -833,6 +833,9 @@ void CodeEdit::_cut_internal(int p_caret) {
delete_selection(p_caret);
return;
}
+ if (!is_empty_selection_clipboard_enabled()) {
+ return;
+ }
if (p_caret == -1) {
delete_lines();
} else {
diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h
index 84e9d4542d..94193ccf74 100644
--- a/scene/gui/color_mode.h
+++ b/scene/gui/color_mode.h
@@ -50,14 +50,14 @@ public:
virtual Color get_color() const = 0;
- virtual void _value_changed(){};
+ virtual void _value_changed() {}
virtual void slider_draw(int p_which) = 0;
virtual bool apply_theme() const { return false; }
virtual ColorPicker::PickerShapeType get_shape_override() const { return ColorPicker::SHAPE_MAX; }
ColorMode(ColorPicker *p_color_picker);
- virtual ~ColorMode(){};
+ virtual ~ColorMode() {}
};
class ColorModeHSV : public ColorMode {
@@ -81,7 +81,7 @@ public:
virtual void slider_draw(int p_which) override;
ColorModeHSV(ColorPicker *p_color_picker) :
- ColorMode(p_color_picker){};
+ ColorMode(p_color_picker) {}
};
class ColorModeRGB : public ColorMode {
@@ -100,7 +100,7 @@ public:
virtual void slider_draw(int p_which) override;
ColorModeRGB(ColorPicker *p_color_picker) :
- ColorMode(p_color_picker){};
+ ColorMode(p_color_picker) {}
};
class ColorModeRAW : public ColorMode {
@@ -122,7 +122,7 @@ public:
virtual bool apply_theme() const override;
ColorModeRAW(ColorPicker *p_color_picker) :
- ColorMode(p_color_picker){};
+ ColorMode(p_color_picker) {}
};
class ColorModeOKHSL : public ColorMode {
@@ -147,9 +147,9 @@ public:
virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; }
ColorModeOKHSL(ColorPicker *p_color_picker) :
- ColorMode(p_color_picker){};
+ ColorMode(p_color_picker) {}
- ~ColorModeOKHSL(){};
+ ~ColorModeOKHSL() {}
};
#endif // COLOR_MODE_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index c92dcbc153..fe4c91cb56 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -33,8 +33,6 @@
#include "core/input/input.h"
#include "core/io/image.h"
#include "core/math/color.h"
-#include "core/os/keyboard.h"
-#include "core/os/os.h"
#include "scene/gui/color_mode.h"
#include "scene/gui/margin_container.h"
#include "scene/resources/image_texture.h"
@@ -42,7 +40,6 @@
#include "scene/resources/style_box_texture.h"
#include "scene/theme/theme_db.h"
#include "servers/display_server.h"
-#include "thirdparty/misc/ok_color.h"
#include "thirdparty/misc/ok_color_shader.h"
List<Color> ColorPicker::preset_cache;
@@ -248,7 +245,21 @@ void ColorPicker::finish_shaders() {
}
void ColorPicker::set_focus_on_line_edit() {
- callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
+ bool has_hardware_keyboard = true;
+#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
+ has_hardware_keyboard = DisplayServer::get_singleton()->has_hardware_keyboard();
+#endif // ANDROID_ENABLED || IOS_ENABLED
+ if (has_hardware_keyboard) {
+ callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
+ } else {
+ // A hack to avoid showing the virtual keyboard when the ColorPicker window popups and
+ // no hardware keyboard is detected on Android and IOS.
+ // This will only focus the LineEdit without editing, the virtual keyboard will only be visible when
+ // we touch the LineEdit to enter edit mode.
+ callable_mp(c_text, &LineEdit::set_editable).call_deferred(false);
+ callable_mp((Control *)c_text, &Control::grab_focus).call_deferred();
+ callable_mp(c_text, &LineEdit::set_editable).call_deferred(true);
+ }
}
void ColorPicker::_update_controls() {
@@ -1567,7 +1578,7 @@ void ColorPicker::_pick_button_pressed_legacy() {
picker_preview_label = memnew(Label);
picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
- picker_preview_label->set_text("Color Picking active");
+ picker_preview_label->set_text(ETR("Color Picking active"));
picker_preview->add_child(picker_preview_label);
picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
@@ -1905,7 +1916,7 @@ ColorPicker::ColorPicker() {
mode_popup->add_radio_check_item(modes[i]->get_name(), i);
}
mode_popup->add_separator();
- mode_popup->add_check_item("Colorized Sliders", MODE_MAX);
+ mode_popup->add_check_item(ETR("Colorized Sliders"), MODE_MAX);
mode_popup->set_item_checked(current_mode, true);
mode_popup->set_item_checked(MODE_MAX + 1, true);
mode_popup->connect(SceneStringName(id_pressed), callable_mp(this, &ColorPicker::_set_mode_popup_value));
@@ -1933,7 +1944,7 @@ ColorPicker::ColorPicker() {
hex_hbc->set_alignment(ALIGNMENT_BEGIN);
vbr->add_child(hex_hbc);
- hex_hbc->add_child(memnew(Label("Hex")));
+ hex_hbc->add_child(memnew(Label(ETR("Hex"))));
text_type = memnew(Button);
hex_hbc->add_child(text_type);
@@ -1997,8 +2008,7 @@ ColorPicker::ColorPicker() {
preset_group.instantiate();
- btn_preset = memnew(Button);
- btn_preset->set_text("Swatches");
+ btn_preset = memnew(Button(ETR("Swatches")));
btn_preset->set_flat(true);
btn_preset->set_toggle_mode(true);
btn_preset->set_focus_mode(FOCUS_NONE);
@@ -2014,8 +2024,7 @@ ColorPicker::ColorPicker() {
recent_preset_group.instantiate();
- btn_recent_preset = memnew(Button);
- btn_recent_preset->set_text("Recent Colors");
+ btn_recent_preset = memnew(Button(ETR("Recent Colors")));
btn_recent_preset->set_flat(true);
btn_recent_preset->set_toggle_mode(true);
btn_recent_preset->set_focus_mode(FOCUS_NONE);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index cecddebe88..c1d197ea9b 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -33,12 +33,8 @@
#include "container.h"
#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/string/print_string.h"
#include "core/string/translation_server.h"
-#include "scene/gui/label.h"
-#include "scene/gui/panel.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
@@ -3161,6 +3157,16 @@ bool Control::is_auto_translating() const {
}
#endif
+void Control::set_tooltip_auto_translate_mode(AutoTranslateMode p_mode) {
+ ERR_MAIN_THREAD_GUARD;
+ data.tooltip_auto_translate_mode = p_mode;
+}
+
+Node::AutoTranslateMode Control::get_tooltip_auto_translate_mode() const {
+ ERR_READ_THREAD_GUARD_V(AUTO_TRANSLATE_MODE_INHERIT);
+ return data.tooltip_auto_translate_mode;
+}
+
// Extra properties.
void Control::set_tooltip_text(const String &p_hint) {
@@ -3510,6 +3516,8 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_grow_direction", "direction"), &Control::set_v_grow_direction);
ClassDB::bind_method(D_METHOD("get_v_grow_direction"), &Control::get_v_grow_direction);
+ ClassDB::bind_method(D_METHOD("set_tooltip_auto_translate_mode", "mode"), &Control::set_tooltip_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("get_tooltip_auto_translate_mode"), &Control::get_tooltip_auto_translate_mode);
ClassDB::bind_method(D_METHOD("set_tooltip_text", "hint"), &Control::set_tooltip_text);
ClassDB::bind_method(D_METHOD("get_tooltip_text"), &Control::get_tooltip_text);
ClassDB::bind_method(D_METHOD("get_tooltip", "at_position"), &Control::get_tooltip, DEFVAL(Point2()));
@@ -3617,6 +3625,7 @@ void Control::_bind_methods() {
ADD_GROUP("Tooltip", "tooltip_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tooltip_auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_tooltip_auto_translate_mode", "get_tooltip_auto_translate_mode");
ADD_GROUP("Focus", "focus_");
ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 2655b14562..6e1621ce58 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -33,7 +33,6 @@
#include "core/math/transform_2d.h"
#include "core/object/gdvirtual.gen.inc"
-#include "core/templates/rid.h"
#include "scene/main/canvas_item.h"
#include "scene/main/timer.h"
#include "scene/resources/theme.h"
@@ -262,6 +261,7 @@ private:
// Extra properties.
String tooltip;
+ AutoTranslateMode tooltip_auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
} data;
@@ -634,6 +634,9 @@ public:
bool is_auto_translating() const;
#endif
+ void set_tooltip_auto_translate_mode(AutoTranslateMode p_mode);
+ AutoTranslateMode get_tooltip_auto_translate_mode() const;
+
// Extra properties.
void set_tooltip_text(const String &text);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 1fc8586448..d8e9d1bcc0 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -208,6 +208,7 @@ void FileDialog::_notification(int p_what) {
refresh->set_icon(theme_cache.reload);
show_hidden->set_icon(theme_cache.toggle_hidden);
makedir->set_icon(theme_cache.create_folder);
+ show_filename_filter_button->set_icon(theme_cache.toggle_filename_filter);
dir_up->begin_bulk_theme_override();
dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
@@ -251,6 +252,13 @@ void FileDialog::_notification(int p_what) {
makedir->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
makedir->end_bulk_theme_override();
+ show_filename_filter_button->begin_bulk_theme_override();
+ show_filename_filter_button->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
+ show_filename_filter_button->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color);
+ show_filename_filter_button->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color);
+ show_filename_filter_button->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
+ show_filename_filter_button->end_bulk_theme_override();
+
invalidate();
} break;
@@ -277,6 +285,14 @@ void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
}
} break;
+ case Key::F: {
+ if (k->is_command_or_control_pressed()) {
+ show_filename_filter_button->set_pressed(!show_filename_filter_button->is_pressed());
+ } else {
+ handled = false;
+ }
+
+ } break;
case Key::F5: {
invalidate();
} break;
@@ -694,18 +710,24 @@ void FileDialog::update_file_list() {
dirs.sort_custom<FileNoCaseComparator>();
files.sort_custom<FileNoCaseComparator>();
+ String filename_filter_lower = file_name_filter.to_lower();
+
while (!dirs.is_empty()) {
- String &dir_name = dirs.front()->get();
- TreeItem *ti = tree->create_item(root);
- ti->set_text(0, dir_name);
- ti->set_icon(0, theme_cache.folder);
- ti->set_icon_modulate(0, theme_cache.folder_icon_color);
+ const String &dir_name = dirs.front()->get();
+
+ if (filename_filter_lower.is_empty() || dir_name.to_lower().contains(filename_filter_lower)) {
+ TreeItem *ti = tree->create_item(root);
- Dictionary d;
- d["name"] = dir_name;
- d["dir"] = true;
+ ti->set_text(0, dir_name);
+ ti->set_icon(0, theme_cache.folder);
+ ti->set_icon_modulate(0, theme_cache.folder_icon_color);
- ti->set_metadata(0, d);
+ Dictionary d;
+ d["name"] = dir_name;
+ d["dir"] = true;
+
+ ti->set_metadata(0, d);
+ }
dirs.pop_front();
}
@@ -750,7 +772,7 @@ void FileDialog::update_file_list() {
}
}
- if (match) {
+ if (match && (filename_filter_lower.is_empty() || files.front()->get().to_lower().contains(filename_filter_lower))) {
TreeItem *ti = tree->create_item(root);
ti->set_text(0, files.front()->get());
@@ -792,6 +814,26 @@ void FileDialog::_filter_selected(int) {
update_file_list();
}
+void FileDialog::_filename_filter_changed() {
+ update_filename_filter();
+ update_file_list();
+ callable_mp(this, &FileDialog::_tree_select_first).call_deferred();
+}
+
+void FileDialog::_tree_select_first() {
+ if (tree->get_root() && tree->get_root()->get_first_child()) {
+ tree->get_root()->get_first_child()->select(0);
+ }
+}
+
+void FileDialog::_filename_filter_selected() {
+ TreeItem *item = tree->get_selected();
+ if (item) {
+ file->set_text(item->get_text(0));
+ file->emit_signal("text_submitted", file->get_text());
+ }
+}
+
void FileDialog::update_filters() {
filter->clear();
@@ -827,6 +869,30 @@ void FileDialog::update_filters() {
filter->add_item(atr(ETR("All Files")) + " (*)");
}
+void FileDialog::clear_filename_filter() {
+ set_filename_filter("");
+ update_filename_filter_gui();
+ invalidate();
+}
+
+void FileDialog::update_filename_filter_gui() {
+ filename_filter_box->set_visible(show_filename_filter);
+ if (!show_filename_filter) {
+ file_name_filter.clear();
+ }
+ if (filename_filter->get_text() == file_name_filter) {
+ return;
+ }
+ filename_filter->set_text(file_name_filter);
+}
+
+void FileDialog::update_filename_filter() {
+ if (filename_filter->get_text() == file_name_filter) {
+ return;
+ }
+ set_filename_filter(filename_filter->get_text());
+}
+
void FileDialog::clear_filters() {
filters.clear();
update_filters();
@@ -853,10 +919,24 @@ void FileDialog::set_filters(const Vector<String> &p_filters) {
invalidate();
}
+void FileDialog::set_filename_filter(const String &p_filename_filter) {
+ if (file_name_filter == p_filename_filter) {
+ return;
+ }
+ file_name_filter = p_filename_filter;
+ update_filename_filter_gui();
+ emit_signal(SNAME("filename_filter_changed"), filter);
+ invalidate();
+}
+
Vector<String> FileDialog::get_filters() const {
return filters;
}
+String FileDialog::get_filename_filter() const {
+ return file_name_filter;
+}
+
String FileDialog::get_current_dir() const {
return dir->get_text();
}
@@ -1266,6 +1346,9 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &FileDialog::add_filter, DEFVAL(""));
ClassDB::bind_method(D_METHOD("set_filters", "filters"), &FileDialog::set_filters);
ClassDB::bind_method(D_METHOD("get_filters"), &FileDialog::get_filters);
+ ClassDB::bind_method(D_METHOD("clear_filename_filter"), &FileDialog::clear_filename_filter);
+ ClassDB::bind_method(D_METHOD("set_filename_filter", "filter"), &FileDialog::set_filename_filter);
+ ClassDB::bind_method(D_METHOD("get_filename_filter"), &FileDialog::get_filename_filter);
ClassDB::bind_method(D_METHOD("get_option_name", "option"), &FileDialog::get_option_name);
ClassDB::bind_method(D_METHOD("get_option_values", "option"), &FileDialog::get_option_values);
ClassDB::bind_method(D_METHOD("get_option_default", "option"), &FileDialog::get_option_default);
@@ -1305,6 +1388,7 @@ void FileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User Data,File System"), "set_access", "get_access");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_subfolder"), "set_root_subfolder", "get_root_subfolder");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename_filter"), "set_filename_filter", "get_filename_filter");
ADD_ARRAY_COUNT("Options", "option_count", "set_option_count", "get_option_count", "option_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_native_dialog"), "set_use_native_dialog", "get_use_native_dialog");
@@ -1315,6 +1399,7 @@ void FileDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths")));
ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir")));
+ ADD_SIGNAL(MethodInfo("filename_filter_changed", PropertyInfo(Variant::STRING, "filter")));
BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILE);
BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILES);
@@ -1332,6 +1417,7 @@ void FileDialog::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, reload);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, toggle_hidden);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, folder);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, toggle_filename_filter);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, file);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, create_folder);
@@ -1363,6 +1449,26 @@ void FileDialog::set_show_hidden_files(bool p_show) {
invalidate();
}
+void FileDialog::set_show_filename_filter(bool p_show) {
+ if (p_show == show_filename_filter) {
+ return;
+ }
+ if (p_show) {
+ filename_filter->grab_focus();
+ } else {
+ if (filename_filter->has_focus()) {
+ tree->call_deferred("grab_focus");
+ }
+ }
+ show_filename_filter = p_show;
+ update_filename_filter_gui();
+ invalidate();
+}
+
+bool FileDialog::get_show_filename_filter() const {
+ return show_filename_filter;
+}
+
bool FileDialog::is_showing_hidden_files() const {
return show_hidden_files;
}
@@ -1446,6 +1552,14 @@ FileDialog::FileDialog() {
show_hidden->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::set_show_hidden_files));
hbc->add_child(show_hidden);
+ show_filename_filter_button = memnew(Button);
+ show_filename_filter_button->set_theme_type_variation("FlatButton");
+ show_filename_filter_button->set_toggle_mode(true);
+ show_filename_filter_button->set_pressed(false);
+ show_filename_filter_button->set_tooltip_text(RTR("Toggle the visibility of the filter for file names."));
+ show_filename_filter_button->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::set_show_filename_filter));
+ hbc->add_child(show_filename_filter_button);
+
shortcuts_container = memnew(HBoxContainer);
hbc->add_child(shortcuts_container);
@@ -1467,6 +1581,17 @@ FileDialog::FileDialog() {
message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(message);
+ filename_filter_box = memnew(HBoxContainer);
+ filename_filter_box->add_child(memnew(Label(RTR("Filter:"))));
+ filename_filter = memnew(LineEdit);
+ filename_filter->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
+ filename_filter->set_stretch_ratio(4);
+ filename_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ filename_filter->set_clear_button_enabled(true);
+ filename_filter_box->add_child(filename_filter);
+ filename_filter_box->set_visible(false);
+ vbox->add_child(filename_filter_box);
+
file_box = memnew(HBoxContainer);
file_box->add_child(memnew(Label(ETR("File:"))));
file = memnew(LineEdit);
@@ -1495,6 +1620,8 @@ FileDialog::FileDialog() {
tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated));
tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all));
dir->connect("text_submitted", callable_mp(this, &FileDialog::_dir_submitted));
+ filename_filter->connect(SceneStringName(text_changed), callable_mp(this, &FileDialog::_filename_filter_changed).unbind(1));
+ filename_filter->connect("text_submitted", callable_mp(this, &FileDialog::_filename_filter_selected).unbind(1));
file->connect("text_submitted", callable_mp(this, &FileDialog::_file_submitted));
filter->connect(SceneStringName(item_selected), callable_mp(this, &FileDialog::_filter_selected));
@@ -1523,9 +1650,11 @@ FileDialog::FileDialog() {
add_child(exterr, false, INTERNAL_MODE_FRONT);
update_filters();
+ update_filename_filter_gui();
update_dir();
set_hide_on_ok(false);
+ set_size(Size2(640, 360));
if (register_func) {
register_func(this);
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 9680157f0a..6ef60a0f4f 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -80,6 +80,8 @@ private:
HBoxContainer *shortcuts_container = nullptr;
OptionButton *drives = nullptr;
Tree *tree = nullptr;
+ HBoxContainer *filename_filter_box = nullptr;
+ LineEdit *filename_filter = nullptr;
HBoxContainer *file_box = nullptr;
LineEdit *file = nullptr;
OptionButton *filter = nullptr;
@@ -96,8 +98,11 @@ private:
Button *refresh = nullptr;
Button *show_hidden = nullptr;
+ Button *show_filename_filter_button = nullptr;
Vector<String> filters;
+ String file_name_filter;
+ bool show_filename_filter = false;
Vector<String> local_history;
int local_history_pos = 0;
@@ -119,6 +124,7 @@ private:
Ref<Texture2D> back_folder;
Ref<Texture2D> reload;
Ref<Texture2D> toggle_hidden;
+ Ref<Texture2D> toggle_filename_filter;
Ref<Texture2D> folder;
Ref<Texture2D> file;
Ref<Texture2D> create_folder;
@@ -149,6 +155,8 @@ private:
void update_dir();
void update_file_name();
void update_file_list();
+ void update_filename_filter();
+ void update_filename_filter_gui();
void update_filters();
void _focus_file_text();
@@ -164,6 +172,9 @@ private:
void _save_confirm_pressed();
void _cancel_pressed();
void _filter_selected(int);
+ void _filename_filter_changed();
+ void _filename_filter_selected();
+ void _tree_select_first();
void _make_dir();
void _make_dir_confirm();
void _go_up();
@@ -208,6 +219,9 @@ public:
void add_filter(const String &p_filter, const String &p_description = "");
void set_filters(const Vector<String> &p_filters);
Vector<String> get_filters() const;
+ void clear_filename_filter();
+ void set_filename_filter(const String &p_filename_filter);
+ String get_filename_filter() const;
void set_enable_multiple_selection(bool p_enable);
Vector<String> get_selected_files() const;
@@ -253,6 +267,8 @@ public:
void set_show_hidden_files(bool p_show);
bool is_showing_hidden_files() const;
+ void set_show_filename_filter(bool p_show);
+ bool get_show_filename_filter() const;
static void set_default_show_hidden_files(bool p_show);
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
index d32c75fbcd..90d5b6b36d 100644
--- a/scene/gui/flow_container.cpp
+++ b/scene/gui/flow_container.cpp
@@ -261,6 +261,7 @@ void FlowContainer::_resort() {
}
cached_size = (vertical ? ofs.x : ofs.y) + line_height;
cached_line_count = lines_data.size();
+ cached_line_max_child_count = lines_data.size() > 0 ? lines_data[0].child_count : 0;
}
Size2 FlowContainer::get_minimum_size() const {
@@ -339,6 +340,10 @@ int FlowContainer::get_line_count() const {
return cached_line_count;
}
+int FlowContainer::get_line_max_child_count() const {
+ return cached_line_max_child_count;
+}
+
void FlowContainer::set_alignment(AlignmentMode p_alignment) {
if (alignment == p_alignment) {
return;
diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h
index 65ebc89c78..6a00e5b0e5 100644
--- a/scene/gui/flow_container.h
+++ b/scene/gui/flow_container.h
@@ -52,6 +52,8 @@ public:
private:
int cached_size = 0;
int cached_line_count = 0;
+ int cached_line_max_child_count = 0;
+ int cached_items_on_last_row = 0;
bool vertical = false;
bool reverse_fill = false;
@@ -74,6 +76,7 @@ protected:
public:
int get_line_count() const;
+ int get_line_max_child_count() const;
void set_alignment(AlignmentMode p_alignment);
AlignmentMode get_alignment() const;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 11a6411e65..646757008a 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -260,6 +260,8 @@ PackedStringArray GraphEdit::get_configuration_warnings() const {
}
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
+ ERR_FAIL_NULL_V_MSG(connections_layer, FAILED, "connections_layer is missing.");
+
if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
return OK;
}
@@ -313,6 +315,8 @@ bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, con
}
void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
for (const List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) {
if (E->get()->from_node == p_from && E->get()->from_port == p_from_port && E->get()->to_node == p_to && E->get()->to_port == p_to_port) {
connection_map[p_from].erase(E->get());
@@ -356,6 +360,8 @@ void GraphEdit::_scroll_moved(double) {
}
void GraphEdit::_update_scroll_offset() {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
set_block_minimum_size_adjust(true);
for (int i = 0; i < get_child_count(); i++) {
@@ -524,6 +530,8 @@ void GraphEdit::_graph_element_resize_request(const Vector2 &p_new_minsize, Node
}
void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, GraphFrame *p_frame) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
_update_graph_frame(p_frame);
minimap->queue_redraw();
@@ -535,6 +543,7 @@ void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, Gr
void GraphEdit::_graph_element_moved(Node *p_node) {
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
ERR_FAIL_NULL(graph_element);
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
minimap->queue_redraw();
queue_redraw();
@@ -543,6 +552,7 @@ void GraphEdit::_graph_element_moved(Node *p_node) {
}
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
GraphNode *graph_node = Object::cast_to<GraphNode>(p_node);
ERR_FAIL_NULL(graph_node);
@@ -558,6 +568,8 @@ void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
}
void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
// Only invalidate the cache when zooming or the node is moved/resized in graph space.
if (panner->is_panning()) {
return;
@@ -566,7 +578,6 @@ void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
for (Ref<Connection> &c : connection_map[p_node->get_name()]) {
c->_cache.dirty = true;
}
-
connections_layer->queue_redraw();
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
@@ -623,7 +634,9 @@ void GraphEdit::add_child_notify(Node *p_child) {
}
graph_element->connect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from).bind(graph_element));
graph_element->connect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request).bind(graph_element));
- graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
+ if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
+ graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
+ }
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
graph_element->set_scale(Vector2(zoom, zoom));
@@ -640,6 +653,9 @@ void GraphEdit::remove_child_notify(Node *p_child) {
minimap = nullptr;
} else if (p_child == connections_layer) {
connections_layer = nullptr;
+ if (is_inside_tree()) {
+ WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons.");
+ }
}
if (top_layer != nullptr && is_inside_tree()) {
@@ -662,7 +678,9 @@ void GraphEdit::remove_child_notify(Node *p_child) {
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
conn->_cache.dirty = true;
}
- connections_layer->queue_redraw();
+ if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
+ connections_layer->queue_redraw();
+ }
}
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
@@ -1690,6 +1708,8 @@ void GraphEdit::set_selected(Node *p_child) {
}
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
ERR_FAIL_COND(p_ev.is_null());
if (panner->gui_input(p_ev, warped_panning ? get_global_rect() : Rect2())) {
return;
@@ -1999,6 +2019,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
} else if (p_ev->is_action("ui_copy", true)) {
emit_signal(SNAME("copy_nodes_request"));
accept_event();
+ } else if (p_ev->is_action("ui_cut", true)) {
+ emit_signal(SNAME("cut_nodes_request"));
+ accept_event();
} else if (p_ev->is_action("ui_paste", true)) {
emit_signal(SNAME("paste_nodes_request"));
accept_event();
@@ -2022,6 +2045,8 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
h_scrollbar->set_value(h_scrollbar->get_value() - p_scroll_vec.x);
v_scrollbar->set_value(v_scrollbar->get_value() - p_scroll_vec.y);
@@ -2037,6 +2062,8 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE
}
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
for (Ref<Connection> &c : connection_map[p_from]) {
if (c->from_node == p_from && c->from_port == p_from_port && c->to_node == p_to && c->to_port == p_to_port) {
if (!Math::is_equal_approx(c->activity, p_activity)) {
@@ -2053,6 +2080,8 @@ void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_por
}
void GraphEdit::reset_all_connection_activity() {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
bool changed = false;
for (Ref<Connection> &conn : connections) {
if (conn->activity > 0) {
@@ -2067,6 +2096,8 @@ void GraphEdit::reset_all_connection_activity() {
}
void GraphEdit::clear_connections() {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
for (Ref<Connection> &c : connections) {
c->_cache.line->queue_free();
}
@@ -2080,7 +2111,9 @@ void GraphEdit::clear_connections() {
}
void GraphEdit::force_connection_drag_end() {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!");
+
connecting = false;
connecting_valid = false;
minimap->queue_redraw();
@@ -2110,6 +2143,8 @@ void GraphEdit::set_zoom(float p_zoom) {
}
void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
p_zoom = CLAMP(p_zoom, zoom_min, zoom_max);
if (zoom == p_zoom) {
return;
@@ -2518,6 +2553,8 @@ bool GraphEdit::is_showing_arrange_button() const {
}
void GraphEdit::override_connections_shader(const Ref<Shader> &p_shader) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
connections_shader = p_shader;
_invalidate_connection_line_cache();
@@ -2536,6 +2573,8 @@ void GraphEdit::_minimap_toggled() {
}
void GraphEdit::set_connection_lines_curvature(float p_curvature) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
lines_curvature = p_curvature;
_invalidate_connection_line_cache();
connections_layer->queue_redraw();
@@ -2547,7 +2586,9 @@ float GraphEdit::get_connection_lines_curvature() const {
}
void GraphEdit::set_connection_lines_thickness(float p_thickness) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
ERR_FAIL_COND_MSG(p_thickness < 0, "Connection lines thickness must be greater than or equal to 0.");
+
if (lines_thickness == p_thickness) {
return;
}
@@ -2562,6 +2603,8 @@ float GraphEdit::get_connection_lines_thickness() const {
}
void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) {
+ ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
+
if (lines_antialiased == p_antialiased) {
return;
}
@@ -2735,6 +2778,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
+ ADD_SIGNAL(MethodInfo("cut_nodes_request"));
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 72e59bfc8a..8f5023104a 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -208,7 +208,7 @@ void GraphNode::_resort() {
// Avoid negative stretch space.
stretch_diff = MAX(stretch_diff, 0);
- available_stretch_space += stretch_diff - sb_panel->get_margin(SIDE_BOTTOM) - sb_panel->get_margin(SIDE_TOP);
+ available_stretch_space += stretch_diff - sb_panel->get_margin(SIDE_BOTTOM) - sb_panel->get_margin(SIDE_TOP) - titlebar_min_size.height - sb_titlebar->get_minimum_size().height;
// Second pass, discard elements that can't be stretched, this will run while stretchable elements exist.
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index bf16c0699e..f3cf29d0cf 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -32,7 +32,6 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/string/translation.h"
#include "scene/theme/theme_db.h"
void ItemList::_shape_text(int p_idx) {
@@ -58,12 +57,12 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo
Item item;
item.icon = p_texture;
item.text = p_item;
- item.xl_text = atr(p_item);
item.selectable = p_selectable;
items.push_back(item);
int item_id = items.size() - 1;
- _shape_text(items.size() - 1);
+ items.write[item_id].xl_text = _atr(item_id, p_item);
+ _shape_text(item_id);
queue_redraw();
shape_changed = true;
@@ -95,7 +94,7 @@ void ItemList::set_item_text(int p_idx, const String &p_text) {
}
items.write[p_idx].text = p_text;
- items.write[p_idx].xl_text = atr(p_text);
+ items.write[p_idx].xl_text = _atr(p_idx, p_text);
_shape_text(p_idx);
queue_redraw();
shape_changed = true;
@@ -141,6 +140,24 @@ String ItemList::get_item_language(int p_idx) const {
return items[p_idx].language;
}
+void ItemList::set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
+ ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].auto_translate_mode != p_mode) {
+ items.write[p_idx].auto_translate_mode = p_mode;
+ items.write[p_idx].xl_text = _atr(p_idx, items[p_idx].text);
+ _shape_text(p_idx);
+ queue_redraw();
+ }
+}
+
+Node::AutoTranslateMode ItemList::get_item_auto_translate_mode(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), AUTO_TRANSLATE_MODE_INHERIT);
+ return items[p_idx].auto_translate_mode;
+}
+
void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
if (p_idx < 0) {
p_idx += get_item_count();
@@ -1022,7 +1039,7 @@ void ItemList::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < items.size(); i++) {
- items.write[i].xl_text = atr(items[i].text);
+ items.write[i].xl_text = _atr(i, items[i].text);
_shape_text(i);
}
shape_changed = true;
@@ -1039,7 +1056,7 @@ void ItemList::_notification(int p_what) {
scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.panel_style->get_margin(SIDE_BOTTOM));
Size2 size = get_size();
- int width = size.width - theme_cache.panel_style->get_minimum_size().width;
+ int width = size.width - theme_cache.panel_style->get_margin(SIDE_RIGHT);
if (scroll_bar->is_visible()) {
width -= scroll_bar_minwidth;
}
@@ -1192,9 +1209,9 @@ void ItemList::_notification(int p_what) {
Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
if (icon_mode == ICON_MODE_TOP) {
- pos.y += theme_cache.v_separation / 2;
+ pos.y += MAX(theme_cache.v_separation, 0) / 2;
} else {
- pos.x += theme_cache.h_separation / 2;
+ pos.x += MAX(theme_cache.h_separation, 0) / 2;
}
if (icon_mode == ICON_MODE_TOP) {
@@ -1243,8 +1260,8 @@ void ItemList::_notification(int p_what) {
}
Point2 draw_pos = items[i].rect_cache.position;
- draw_pos.x += theme_cache.h_separation / 2;
- draw_pos.y += theme_cache.v_separation / 2;
+ draw_pos.x += MAX(theme_cache.h_separation, 0) / 2;
+ draw_pos.y += MAX(theme_cache.v_separation, 0) / 2;
if (rtl) {
draw_pos.x = size.width - draw_pos.x - tag_icon_size.x;
}
@@ -1283,8 +1300,7 @@ void ItemList::_notification(int p_what) {
text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position;
- text_ofs.x += theme_cache.h_separation / 2;
- text_ofs.y += theme_cache.v_separation / 2;
+ text_ofs.y += MAX(theme_cache.v_separation, 0) / 2;
if (rtl) {
text_ofs.x = size.width - text_ofs.x - max_len;
@@ -1292,7 +1308,7 @@ void ItemList::_notification(int p_what) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- float text_w = items[i].rect_cache.size.width - theme_cache.h_separation;
+ float text_w = items[i].rect_cache.size.width;
items.write[i].text_buf->set_width(text_w);
if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
@@ -1307,17 +1323,17 @@ void ItemList::_notification(int p_what) {
if (icon_mode == ICON_MODE_TOP) {
text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
- text_ofs.x += theme_cache.h_separation / 2;
- text_ofs.y += theme_cache.v_separation / 2;
+ text_ofs.x += MAX(theme_cache.h_separation, 0) / 2;
+ text_ofs.y += MAX(theme_cache.v_separation, 0) / 2;
} else {
text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
- text_ofs.x += theme_cache.h_separation / 2;
+ text_ofs.x += MAX(theme_cache.h_separation, 0) / 2;
}
text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position;
- float text_w = width - text_ofs.x - theme_cache.h_separation;
+ float text_w = width - text_ofs.x;
items.write[i].text_buf->set_width(text_w);
if (rtl) {
@@ -1410,14 +1426,14 @@ void ItemList::force_update_list_size() {
max_column_width = MAX(max_column_width, minsize.x);
// Elements need to adapt to the selected size.
- minsize.y += theme_cache.v_separation;
- minsize.x += theme_cache.h_separation;
+ minsize.y += MAX(theme_cache.v_separation, 0);
+ minsize.x += MAX(theme_cache.h_separation, 0);
items.write[i].rect_cache.size = minsize;
items.write[i].min_rect_cache.size = minsize;
}
- int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - scroll_bar_minwidth;
+ int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width;
//2-attempt best fit
current_columns = 0x7FFFFFFF;
@@ -1430,12 +1446,13 @@ void ItemList::force_update_list_size() {
bool all_fit = true;
Vector2 ofs;
int col = 0;
+ int max_w = 0;
int max_h = 0;
separators.clear();
for (int i = 0; i < items.size(); i++) {
- if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
+ if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size && !auto_width) {
// Went past.
current_columns = MAX(col, 1);
all_fit = false;
@@ -1443,7 +1460,7 @@ void ItemList::force_update_list_size() {
}
if (same_column_width) {
- items.write[i].rect_cache.size.x = max_column_width + theme_cache.h_separation;
+ items.write[i].rect_cache.size.x = max_column_width + MAX(theme_cache.h_separation, 0);
}
items.write[i].rect_cache.position = ofs;
@@ -1461,6 +1478,7 @@ void ItemList::force_update_list_size() {
items.write[j].rect_cache.size.y = max_h;
}
+ max_w = MAX(max_w, ofs.x);
ofs.x = 0;
ofs.y += max_h;
col = 0;
@@ -1468,22 +1486,30 @@ void ItemList::force_update_list_size() {
}
}
+ float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
+ float max = MAX(page, ofs.y + max_h);
+ if (page >= max) {
+ fit_size -= scroll_bar_minwidth;
+ }
+
if (all_fit) {
for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
items.write[j].rect_cache.size.y = max_h;
}
- float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
- float max = MAX(page, ofs.y + max_h);
if (auto_height) {
auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height;
}
+ if (auto_width) {
+ auto_width_value = max_w + theme_cache.panel_style->get_minimum_size().width;
+ }
scroll_bar->set_max(max);
scroll_bar->set_page(page);
if (max <= page) {
scroll_bar->set_value(0);
scroll_bar->hide();
} else {
+ auto_width_value += scroll_bar_minwidth;
scroll_bar->show();
if (do_autoscroll_to_bottom) {
@@ -1509,6 +1535,23 @@ void ItemList::_mouse_exited() {
}
}
+String ItemList::_atr(int p_idx, const String &p_text) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), atr(p_text));
+ switch (items[p_idx].auto_translate_mode) {
+ case AUTO_TRANSLATE_MODE_INHERIT: {
+ return atr(p_text);
+ } break;
+ case AUTO_TRANSLATE_MODE_ALWAYS: {
+ return tr(p_text);
+ } break;
+ case AUTO_TRANSLATE_MODE_DISABLED: {
+ return p_text;
+ } break;
+ }
+
+ ERR_FAIL_V_MSG(atr(p_text), "Unexpected auto translate mode: " + itos(items[p_idx].auto_translate_mode));
+}
+
int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
Vector2 pos = p_pos;
pos -= theme_cache.panel_style->get_offset();
@@ -1667,16 +1710,35 @@ bool ItemList::is_anything_selected() {
}
Size2 ItemList::get_minimum_size() const {
+ Size2 min_size;
+ if (auto_width) {
+ min_size.x = auto_width_value;
+ }
+
if (auto_height) {
- return Size2(0, auto_height_value);
+ min_size.y = auto_height_value;
}
- return Size2();
+ return min_size;
}
void ItemList::set_autoscroll_to_bottom(const bool p_enable) {
do_autoscroll_to_bottom = p_enable;
}
+void ItemList::set_auto_width(bool p_enable) {
+ if (auto_width == p_enable) {
+ return;
+ }
+
+ auto_width = p_enable;
+ shape_changed = true;
+ queue_redraw();
+}
+
+bool ItemList::has_auto_width() const {
+ return auto_width;
+}
+
void ItemList::set_auto_height(bool p_enable) {
if (auto_height == p_enable) {
return;
@@ -1748,6 +1810,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &ItemList::set_item_language);
ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &ItemList::get_item_language);
+ ClassDB::bind_method(D_METHOD("set_item_auto_translate_mode", "idx", "mode"), &ItemList::set_item_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("get_item_auto_translate_mode", "idx"), &ItemList::get_item_auto_translate_mode);
+
ClassDB::bind_method(D_METHOD("set_item_icon_transposed", "idx", "transposed"), &ItemList::set_item_icon_transposed);
ClassDB::bind_method(D_METHOD("is_item_icon_transposed", "idx"), &ItemList::is_item_icon_transposed);
@@ -1829,6 +1894,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &ItemList::set_allow_search);
ClassDB::bind_method(D_METHOD("get_allow_search"), &ItemList::get_allow_search);
+ ClassDB::bind_method(D_METHOD("set_auto_width", "enable"), &ItemList::set_auto_width);
+ ClassDB::bind_method(D_METHOD("has_auto_width"), &ItemList::has_auto_width);
+
ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height);
ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height);
@@ -1850,6 +1918,7 @@ void ItemList::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_width"), "set_auto_width", "has_auto_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index a20688b3d0..0836ea33d5 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -62,6 +62,7 @@ private:
Ref<TextParagraph> text_buf;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
+ AutoTranslateMode auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
bool selectable = true;
bool selected = false;
@@ -99,6 +100,9 @@ private:
bool same_column_width = false;
bool allow_search = true;
+ bool auto_width = false;
+ float auto_width_value = 0.0;
+
bool auto_height = false;
float auto_height_value = 0.0;
@@ -159,6 +163,8 @@ private:
void _shape_text(int p_idx);
void _mouse_exited();
+ String _atr(int p_idx, const String &p_text) const;
+
protected:
void _notification(int p_what);
bool _set(const StringName &p_name, const Variant &p_value);
@@ -183,6 +189,9 @@ public:
void set_item_language(int p_idx, const String &p_language);
String get_item_language(int p_idx) const;
+ void set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode);
+ AutoTranslateMode get_item_auto_translate_mode(int p_idx) const;
+
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_item_icon(int p_idx) const;
@@ -285,6 +294,9 @@ public:
void set_icon_scale(real_t p_scale);
real_t get_icon_scale() const;
+ void set_auto_width(bool p_enable);
+ bool has_auto_width() const;
+
void set_auto_height(bool p_enable);
bool has_auto_height() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 1a066b0728..9967805134 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -45,28 +45,37 @@
#include "editor/editor_settings.h"
#endif
-void LineEdit::_edit() {
+void LineEdit::edit() {
if (!is_inside_tree()) {
return;
}
if (!has_focus()) {
grab_focus();
+ return;
}
if (!editable || editing) {
return;
}
+ if (select_all_on_focus) {
+ if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
+ // Select all when the mouse button is up.
+ pending_select_all_on_focus = true;
+ } else {
+ select_all();
+ }
+ }
+
editing = true;
_validate_caret_can_draw();
show_virtual_keyboard();
queue_redraw();
- emit_signal(SNAME("editing_toggled"), true);
}
-void LineEdit::_unedit() {
+void LineEdit::unedit() {
if (!editing) {
return;
}
@@ -84,8 +93,6 @@ void LineEdit::_unedit() {
if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
deselect();
}
-
- emit_signal(SNAME("editing_toggled"), false);
}
bool LineEdit::is_editing() const {
@@ -390,7 +397,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (editable && !editing) {
- _edit();
+ edit();
+ emit_signal(SNAME("editing_toggled"), true);
}
accept_event();
@@ -406,7 +414,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
set_caret_at_pixel_pos(b->get_position().x);
if (!editing) {
- _edit();
+ edit();
+ emit_signal(SNAME("editing_toggled"), true);
}
if (!paste_buffer.is_empty()) {
@@ -506,7 +515,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (editable && !editing) {
- _edit();
+ edit();
+ emit_signal(SNAME("editing_toggled"), true);
return;
}
queue_redraw();
@@ -599,7 +609,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (editable && !editing && k->is_action_pressed("ui_text_submit", false)) {
- _edit();
+ edit();
+ emit_signal(SNAME("editing_toggled"), true);
+ accept_event();
return;
}
@@ -734,7 +746,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (editing) {
- _unedit();
+ unedit();
+ emit_signal(SNAME("editing_toggled"), false);
}
accept_event();
@@ -743,7 +756,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
if (k->is_action("ui_cancel")) {
if (editing) {
- _unedit();
+ unedit();
+ emit_signal(SNAME("editing_toggled"), false);
}
accept_event();
@@ -921,6 +935,7 @@ Variant LineEdit::get_drag_data(const Point2 &p_point) {
String t = get_selected_text();
Label *l = memnew(Label);
l->set_text(t);
+ l->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Don't translate user input.
set_drag_preview(l);
return t;
}
@@ -1331,24 +1346,17 @@ void LineEdit::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (select_all_on_focus) {
- if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
- // Select all when the mouse button is up.
- pending_select_all_on_focus = true;
- } else {
- select_all();
- }
- }
-
// Only allow editing if the LineEdit is not focused with arrow keys.
if (!(Input::get_singleton()->is_action_pressed("ui_up") || Input::get_singleton()->is_action_pressed("ui_down") || Input::get_singleton()->is_action_pressed("ui_left") || Input::get_singleton()->is_action_pressed("ui_right"))) {
- _edit();
+ edit();
+ emit_signal(SNAME("editing_toggled"), true);
}
} break;
case NOTIFICATION_FOCUS_EXIT: {
if (editing) {
- _unedit();
+ unedit();
+ emit_signal(SNAME("editing_toggled"), false);
}
} break;
@@ -2137,7 +2145,8 @@ void LineEdit::set_editable(bool p_editable) {
editable = p_editable;
if (!editable && editing) {
- _unedit();
+ unedit();
+ emit_signal(SNAME("editing_toggled"), false);
}
_validate_caret_can_draw();
@@ -2758,11 +2767,15 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment);
ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("edit"), &LineEdit::edit);
+ ClassDB::bind_method(D_METHOD("unedit"), &LineEdit::unedit);
ClassDB::bind_method(D_METHOD("is_editing"), &LineEdit::is_editing);
ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear);
ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all);
ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect);
+ ClassDB::bind_method(D_METHOD("has_undo"), &LineEdit::has_undo);
+ ClassDB::bind_method(D_METHOD("has_redo"), &LineEdit::has_redo);
ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection);
ClassDB::bind_method(D_METHOD("get_selected_text"), &LineEdit::get_selected_text);
ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index ac7436646b..9253dd8711 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -207,9 +207,6 @@ private:
float base_scale = 1.0;
} theme_cache;
- void _edit();
- void _unedit();
-
void _close_ime_window();
void _update_ime_window_position();
@@ -265,6 +262,8 @@ protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
public:
+ void edit();
+ void unedit();
bool is_editing() const;
bool has_ime_text() const;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 0c3c90d070..26141663c1 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2067,16 +2067,27 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
}
+ bool scroll_value_modified = false;
+ double prev_scroll = vscroll->get_value();
+
if (b->get_button_index() == MouseButton::WHEEL_UP) {
if (scroll_active) {
vscroll->scroll(-vscroll->get_page() * b->get_factor() * 0.5 / 8);
+ scroll_value_modified = true;
}
}
if (b->get_button_index() == MouseButton::WHEEL_DOWN) {
if (scroll_active) {
vscroll->scroll(vscroll->get_page() * b->get_factor() * 0.5 / 8);
+ scroll_value_modified = true;
}
}
+
+ if (scroll_value_modified && vscroll->get_value() != prev_scroll) {
+ accept_event();
+ return;
+ }
+
if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
_update_context_menu();
menu->set_position(get_screen_position() + b->get_position());
@@ -5431,6 +5442,7 @@ Variant RichTextLabel::get_drag_data(const Point2 &p_point) {
String t = get_selected_text();
Label *l = memnew(Label);
l->set_text(t);
+ l->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Text is already translated.
set_drag_preview(l);
return t;
}
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index af9f08e389..eb69dab478 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -472,7 +472,7 @@ double ScrollBar::get_area_size() const {
}
double ScrollBar::get_grabber_offset() const {
- return (get_area_size()) * get_as_ratio();
+ return get_area_size() * get_as_ratio();
}
Size2 ScrollBar::get_minimum_size() const {
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index f1902bade4..1ac0e8b59f 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -58,7 +58,7 @@ Size2 ScrollContainer::get_minimum_size() const {
if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.x = largest_child_min_size.x;
- bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y);
+ bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || vertical_scroll_mode == SCROLL_MODE_RESERVE || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y);
if (v_scroll_show && v_scroll->get_parent() == this) {
min_size.x += v_scroll->get_minimum_size().x;
}
@@ -66,7 +66,7 @@ Size2 ScrollContainer::get_minimum_size() const {
if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.y = largest_child_min_size.y;
- bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x);
+ bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || horizontal_scroll_mode == SCROLL_MODE_RESERVE || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x);
if (h_scroll_show && h_scroll->get_parent() == this) {
min_size.y += h_scroll->get_minimum_size().y;
}
@@ -92,6 +92,15 @@ void ScrollContainer::_cancel_drag() {
}
}
+bool ScrollContainer::_is_h_scroll_visible() const {
+ // Scrolls may have been moved out for reasons.
+ return h_scroll->is_visible() && h_scroll->get_parent() == this;
+}
+
+bool ScrollContainer::_is_v_scroll_visible() const {
+ return v_scroll->is_visible() && v_scroll->get_parent() == this;
+}
+
void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
ERR_FAIL_COND(p_gui_input.is_null());
@@ -298,11 +307,11 @@ void ScrollContainer::_reposition_children() {
ofs += theme_cache.panel_style->get_offset();
bool rtl = is_layout_rtl();
- if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
+ if (_is_h_scroll_visible() || horizontal_scroll_mode == SCROLL_MODE_RESERVE) {
size.y -= h_scroll->get_minimum_size().y;
}
- if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
+ if (_is_v_scroll_visible() || vertical_scroll_mode == SCROLL_MODE_RESERVE) {
size.x -= v_scroll->get_minimum_size().x;
}
@@ -324,7 +333,7 @@ void ScrollContainer::_reposition_children() {
r.size.height = MAX(size.height, minsize.height);
}
r.position += ofs;
- if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) {
+ if (rtl && _is_v_scroll_visible()) {
r.position.x += v_scroll->get_minimum_size().x;
}
r.position = r.position.floor();
@@ -436,14 +445,14 @@ void ScrollContainer::update_scrollbars() {
Size2 hmin = h_scroll->get_combined_minimum_size();
Size2 vmin = v_scroll->get_combined_minimum_size();
- h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.width > size.width));
- v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.height > size.height));
+ h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((horizontal_scroll_mode == SCROLL_MODE_AUTO || horizontal_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.width > size.width));
+ v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((vertical_scroll_mode == SCROLL_MODE_AUTO || vertical_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.height > size.height));
h_scroll->set_max(largest_child_min_size.width);
- h_scroll->set_page((v_scroll->is_visible() && v_scroll->get_parent() == this) ? size.width - vmin.width : size.width);
+ h_scroll->set_page(_is_v_scroll_visible() ? size.width - vmin.width : size.width);
v_scroll->set_max(largest_child_min_size.height);
- v_scroll->set_page((h_scroll->is_visible() && h_scroll->get_parent() == this) ? size.height - hmin.height : size.height);
+ v_scroll->set_page(_is_h_scroll_visible() ? size.height - hmin.height : size.height);
// Avoid scrollbar overlapping.
_updating_scrollbars = true;
@@ -538,7 +547,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
int found = 0;
for (int i = 0; i < get_child_count(); i++) {
- Control *c = as_sortable_control(get_child(i));
+ Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
if (!c) {
continue;
}
@@ -603,14 +612,15 @@ void ScrollContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_horizontal_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_horizontal_custom_step", "get_horizontal_custom_step");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_vertical_custom_step", "get_vertical_custom_step");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
BIND_ENUM_CONSTANT(SCROLL_MODE_DISABLED);
BIND_ENUM_CONSTANT(SCROLL_MODE_AUTO);
BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_ALWAYS);
BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_RESERVE);
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel");
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 02146618cd..afd3c8bd57 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -44,6 +44,7 @@ public:
SCROLL_MODE_AUTO,
SCROLL_MODE_SHOW_ALWAYS,
SCROLL_MODE_SHOW_NEVER,
+ SCROLL_MODE_RESERVE,
};
private:
@@ -75,6 +76,9 @@ private:
void _cancel_drag();
+ bool _is_h_scroll_visible() const;
+ bool _is_v_scroll_visible() const;
+
protected:
Size2 get_minimum_size() const override;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 8ab9b4c1cd..be03db526d 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -32,6 +32,7 @@
#include "scene/gui/label.h"
#include "scene/gui/margin_container.h"
+#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
@@ -39,7 +40,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
- if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) {
+ if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
return;
}
@@ -48,8 +49,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
- sc->_compute_middle_sep(true);
+ sc->_compute_split_offset(true);
dragging = true;
+ sc->emit_signal(SNAME("drag_started"));
drag_ofs = sc->split_offset;
if (sc->vertical) {
drag_from = get_transform().xform(mb->get_position()).y;
@@ -59,6 +61,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
} else {
dragging = false;
queue_redraw();
+ sc->emit_signal(SNAME("drag_ended"));
}
}
}
@@ -76,7 +79,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
} else {
sc->split_offset = drag_ofs + ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from);
}
- sc->_compute_middle_sep(true);
+ sc->_compute_split_offset(true);
sc->queue_sort();
sc->emit_signal(SNAME("dragged"), sc->get_split_offset());
}
@@ -84,11 +87,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
Control::CursorShape SplitContainerDragger::get_cursor_shape(const Point2 &p_pos) const {
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
-
- if (!sc->collapsed && sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE) {
+ if (!sc->collapsed && sc->dragging_enabled) {
return (sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT);
}
-
return Control::get_cursor_shape(p_pos);
}
@@ -101,7 +102,6 @@ void SplitContainerDragger::_notification(int p_what) {
queue_redraw();
}
} break;
-
case NOTIFICATION_MOUSE_EXIT: {
mouse_inside = false;
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
@@ -109,22 +109,25 @@ void SplitContainerDragger::_notification(int p_what) {
queue_redraw();
}
} break;
-
case NOTIFICATION_DRAW: {
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
- if (!dragging && !mouse_inside && sc->theme_cache.autohide) {
- return;
+ draw_style_box(sc->theme_cache.split_bar_background, split_bar_rect);
+ if (sc->dragger_visibility == sc->DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide)) {
+ Ref<Texture2D> tex = sc->_get_grabber_icon();
+ float available_size = sc->vertical ? (sc->get_size().x - tex->get_size().x) : (sc->get_size().y - tex->get_size().y);
+ if (available_size - sc->drag_area_margin_begin - sc->drag_area_margin_end > 0) { // Draw the grabber only if it fits.
+ draw_texture(tex, (split_bar_rect.get_position() + (split_bar_rect.get_size() - tex->get_size()) * 0.5));
+ }
+ }
+ if (sc->show_drag_area && Engine::get_singleton()->is_editor_hint()) {
+ draw_rect(Rect2(Vector2(0, 0), get_size()), sc->dragging_enabled ? Color(1, 1, 0, 0.3) : Color(1, 0, 0, 0.3));
}
-
- Ref<Texture2D> tex = sc->_get_grabber_icon();
- draw_texture(tex, (get_size() - tex->get_size()) / 2);
} break;
}
}
Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode) const {
int idx = 0;
-
for (int i = 0; i < get_child_count(false); i++) {
Control *c = as_sortable_control(get_child(i, false), p_visibility_mode);
if (!c) {
@@ -137,7 +140,6 @@ Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_
idx++;
}
-
return nullptr;
}
@@ -153,45 +155,48 @@ Ref<Texture2D> SplitContainer::_get_grabber_icon() const {
}
}
-void SplitContainer::_compute_middle_sep(bool p_clamp) {
+int SplitContainer::_get_separation() const {
+ if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) {
+ return 0;
+ }
+ // DRAGGER_VISIBLE or DRAGGER_HIDDEN.
+ Ref<Texture2D> g = _get_grabber_icon();
+ return MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width());
+}
+
+void SplitContainer::_compute_split_offset(bool p_clamp) {
Control *first = _get_sortable_child(0);
Control *second = _get_sortable_child(1);
+ int axis_index = vertical ? 1 : 0;
+ int size = get_size()[axis_index];
+ int sep = _get_separation();
- // Determine expanded children.
- bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
- bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
-
- // Compute the minimum size.
- int axis = vertical ? 1 : 0;
- int size = get_size()[axis];
- int ms_first = first->get_combined_minimum_size()[axis];
- int ms_second = second->get_combined_minimum_size()[axis];
-
- // Determine the separation between items.
- Ref<Texture2D> g = _get_grabber_icon();
- int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
-
- // Compute the wished separation_point.
- int wished_middle_sep = 0;
+ // Compute the wished size.
+ int wished_size = 0;
int split_offset_with_collapse = 0;
if (!collapsed) {
split_offset_with_collapse = split_offset;
}
- if (first_expanded && second_expanded) {
+ bool first_is_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
+ bool second_is_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
+
+ if (first_is_expanded && second_is_expanded) {
float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio());
- wished_middle_sep = size * ratio - sep / 2 + split_offset_with_collapse;
- } else if (first_expanded) {
- wished_middle_sep = size - sep + split_offset_with_collapse;
+ wished_size = size * ratio - sep * 0.5 + split_offset_with_collapse;
+ } else if (first_is_expanded) {
+ wished_size = size - sep + split_offset_with_collapse;
} else {
- wished_middle_sep = split_offset_with_collapse;
+ wished_size = split_offset_with_collapse;
}
- // Clamp the middle sep to acceptatble values.
- middle_sep = CLAMP(wished_middle_sep, ms_first, size - sep - ms_second);
+ // Clamp the split offset to acceptable values.
+ int first_min_size = first->get_combined_minimum_size()[axis_index];
+ int second_min_size = second->get_combined_minimum_size()[axis_index];
+ computed_split_offset = CLAMP(wished_size, first_min_size, size - sep - second_min_size);
// Clamp the split_offset if requested.
if (p_clamp) {
- split_offset -= wished_middle_sep - middle_sep;
+ split_offset -= wished_size - computed_split_offset;
}
}
@@ -199,8 +204,7 @@ void SplitContainer::_resort() {
Control *first = _get_sortable_child(0);
Control *second = _get_sortable_child(1);
- // If we have only one element.
- if (!first || !second) {
+ if (!first || !second) { // Only one child.
if (first) {
fit_child_in_rect(first, Rect2(Point2(), get_size()));
} else if (second) {
@@ -209,53 +213,50 @@ void SplitContainer::_resort() {
dragging_area_control->hide();
return;
}
+ dragging_area_control->set_visible(!collapsed);
- // If we have more that one.
- _compute_middle_sep(false);
+ _compute_split_offset(false); // This recalculates and sets computed_split_offset.
- // Determine the separation between items.
- Ref<Texture2D> g = _get_grabber_icon();
- int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
+ int sep = _get_separation();
+ bool is_rtl = is_layout_rtl();
- // Move the children, including the dragger.
+ // Move the children.
if (vertical) {
- fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, middle_sep)));
- int sofs = middle_sep + sep;
+ fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, computed_split_offset)));
+ int sofs = computed_split_offset + sep;
fit_child_in_rect(second, Rect2(Point2(0, sofs), Size2(get_size().width, get_size().height - sofs)));
} else {
- if (is_layout_rtl()) {
- middle_sep = get_size().width - middle_sep - sep;
- fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height)));
- int sofs = middle_sep + sep;
+ if (is_rtl) {
+ computed_split_offset = get_size().width - computed_split_offset - sep;
+ fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height)));
+ int sofs = computed_split_offset + sep;
fit_child_in_rect(first, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height)));
} else {
- fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height)));
- int sofs = middle_sep + sep;
+ fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height)));
+ int sofs = computed_split_offset + sep;
fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height)));
}
}
- // Handle the dragger visibility and position.
- if (dragger_visibility == DRAGGER_VISIBLE && !collapsed) {
- dragging_area_control->show();
-
- int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness);
- if (vertical) {
- dragging_area_control->set_rect(Rect2(Point2(0, middle_sep - (dragger_ctrl_size - sep) / 2), Size2(get_size().width, dragger_ctrl_size)));
- } else {
- dragging_area_control->set_rect(Rect2(Point2(middle_sep - (dragger_ctrl_size - sep) / 2, 0), Size2(dragger_ctrl_size, get_size().height)));
- }
-
- dragging_area_control->queue_redraw();
+ dragging_area_control->set_mouse_filter(dragging_enabled ? MOUSE_FILTER_STOP : MOUSE_FILTER_IGNORE);
+ const int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness);
+ float split_bar_offset = (dragger_ctrl_size - sep) * 0.5;
+ if (vertical) {
+ Rect2 split_bar_rect = Rect2(is_rtl ? drag_area_margin_end : drag_area_margin_begin, computed_split_offset, get_size().width - drag_area_margin_begin - drag_area_margin_end, sep);
+ dragging_area_control->set_rect(Rect2(split_bar_rect.position.x, split_bar_rect.position.y - split_bar_offset + drag_area_offset, split_bar_rect.size.x, dragger_ctrl_size));
+ dragging_area_control->split_bar_rect = Rect2(Vector2(0.0, int(split_bar_offset) - drag_area_offset), split_bar_rect.size);
} else {
- dragging_area_control->hide();
+ Rect2 split_bar_rect = Rect2(computed_split_offset, drag_area_margin_begin, sep, get_size().height - drag_area_margin_begin - drag_area_margin_end);
+ dragging_area_control->set_rect(Rect2(split_bar_rect.position.x - split_bar_offset + drag_area_offset * (is_rtl ? -1 : 1), split_bar_rect.position.y, dragger_ctrl_size, split_bar_rect.size.y));
+ dragging_area_control->split_bar_rect = Rect2(Vector2(int(split_bar_offset) - drag_area_offset * (is_rtl ? -1 : 1), 0.0), split_bar_rect.size);
}
+ queue_redraw();
+ dragging_area_control->queue_redraw();
}
Size2 SplitContainer::get_minimum_size() const {
Size2i minimum;
- Ref<Texture2D> g = _get_grabber_icon();
- int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
+ int sep = _get_separation();
for (int i = 0; i < 2; i++) {
Control *child = _get_sortable_child(i, SortableVisbilityMode::VISIBLE);
@@ -297,11 +298,9 @@ void SplitContainer::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
} break;
-
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
-
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
@@ -312,9 +311,7 @@ void SplitContainer::set_split_offset(int p_offset) {
if (split_offset == p_offset) {
return;
}
-
split_offset = p_offset;
-
queue_sort();
}
@@ -326,8 +323,7 @@ void SplitContainer::clamp_split_offset() {
if (!_get_sortable_child(0) || !_get_sortable_child(1)) {
return;
}
-
- _compute_middle_sep(true);
+ _compute_split_offset(true);
queue_sort();
}
@@ -335,7 +331,6 @@ void SplitContainer::set_collapsed(bool p_collapsed) {
if (collapsed == p_collapsed) {
return;
}
-
collapsed = p_collapsed;
queue_sort();
}
@@ -344,7 +339,6 @@ void SplitContainer::set_dragger_visibility(DraggerVisibility p_visibility) {
if (dragger_visibility == p_visibility) {
return;
}
-
dragger_visibility = p_visibility;
queue_sort();
}
@@ -368,6 +362,26 @@ bool SplitContainer::is_vertical() const {
return vertical;
}
+void SplitContainer::set_dragging_enabled(bool p_enabled) {
+ if (dragging_enabled == p_enabled) {
+ return;
+ }
+ dragging_enabled = p_enabled;
+ if (!dragging_enabled && dragging_area_control->dragging) {
+ dragging_area_control->dragging = false;
+ // queue_redraw() is called by _resort().
+ emit_signal(SNAME("drag_ended"));
+ }
+ if (get_viewport()) {
+ get_viewport()->update_mouse_cursor_state();
+ }
+ _resort();
+}
+
+bool SplitContainer::is_dragging_enabled() const {
+ return dragging_enabled;
+}
+
Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
Vector<int> flags;
flags.append(SIZE_FILL);
@@ -392,6 +406,51 @@ Vector<int> SplitContainer::get_allowed_size_flags_vertical() const {
return flags;
}
+void SplitContainer::set_drag_area_margin_begin(int p_margin) {
+ if (drag_area_margin_begin == p_margin) {
+ return;
+ }
+ drag_area_margin_begin = p_margin;
+ queue_sort();
+}
+
+int SplitContainer::get_drag_area_margin_begin() const {
+ return drag_area_margin_begin;
+}
+
+void SplitContainer::set_drag_area_margin_end(int p_margin) {
+ if (drag_area_margin_end == p_margin) {
+ return;
+ }
+ drag_area_margin_end = p_margin;
+ queue_sort();
+}
+
+int SplitContainer::get_drag_area_margin_end() const {
+ return drag_area_margin_end;
+}
+
+void SplitContainer::set_drag_area_offset(int p_offset) {
+ if (drag_area_offset == p_offset) {
+ return;
+ }
+ drag_area_offset = p_offset;
+ queue_sort();
+}
+
+int SplitContainer::get_drag_area_offset() const {
+ return drag_area_offset;
+}
+
+void SplitContainer::set_show_drag_area_enabled(bool p_enabled) {
+ show_drag_area = p_enabled;
+ dragging_area_control->queue_redraw();
+}
+
+bool SplitContainer::is_show_drag_area_enabled() const {
+ return show_drag_area;
+}
+
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
@@ -406,13 +465,39 @@ void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical);
ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical);
+ ClassDB::bind_method(D_METHOD("set_dragging_enabled", "dragging_enabled"), &SplitContainer::set_dragging_enabled);
+ ClassDB::bind_method(D_METHOD("is_dragging_enabled"), &SplitContainer::is_dragging_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_drag_area_margin_begin", "margin"), &SplitContainer::set_drag_area_margin_begin);
+ ClassDB::bind_method(D_METHOD("get_drag_area_margin_begin"), &SplitContainer::get_drag_area_margin_begin);
+
+ ClassDB::bind_method(D_METHOD("set_drag_area_margin_end", "margin"), &SplitContainer::set_drag_area_margin_end);
+ ClassDB::bind_method(D_METHOD("get_drag_area_margin_end"), &SplitContainer::get_drag_area_margin_end);
+
+ ClassDB::bind_method(D_METHOD("set_drag_area_offset", "offset"), &SplitContainer::set_drag_area_offset);
+ ClassDB::bind_method(D_METHOD("get_drag_area_offset"), &SplitContainer::get_drag_area_offset);
+
+ ClassDB::bind_method(D_METHOD("set_drag_area_highlight_in_editor", "drag_area_highlight_in_editor"), &SplitContainer::set_show_drag_area_enabled);
+ ClassDB::bind_method(D_METHOD("is_drag_area_highlight_in_editor_enabled"), &SplitContainer::is_show_drag_area_enabled);
+
+ ClassDB::bind_method(D_METHOD("get_drag_area_control"), &SplitContainer::get_drag_area_control);
+
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
+ ADD_SIGNAL(MethodInfo("drag_started"));
+ ADD_SIGNAL(MethodInfo("drag_ended"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dragging_enabled"), "set_dragging_enabled", "is_dragging_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
+ ADD_GROUP("Drag Area", "drag_area_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_begin", "get_drag_area_margin_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_end", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_end", "get_drag_area_margin_end");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_offset", "get_drag_area_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_area_highlight_in_editor"), "set_drag_area_highlight_in_editor", "is_drag_area_highlight_in_editor_enabled");
+
BIND_ENUM_CONSTANT(DRAGGER_VISIBLE);
BIND_ENUM_CONSTANT(DRAGGER_HIDDEN);
BIND_ENUM_CONSTANT(DRAGGER_HIDDEN_COLLAPSED);
@@ -423,6 +508,7 @@ void SplitContainer::_bind_methods() {
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon, "grabber");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_h, "h_grabber");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_v, "v_grabber");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SplitContainer, split_bar_background, "split_bar_background");
}
SplitContainer::SplitContainer(bool p_vertical) {
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index db870554c2..2bba96b4b8 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -35,6 +35,8 @@
class SplitContainerDragger : public Control {
GDCLASS(SplitContainerDragger, Control);
+ friend class SplitContainer;
+ Rect2 split_bar_rect;
protected:
void _notification(int p_what);
@@ -62,11 +64,16 @@ public:
};
private:
+ int show_drag_area = false;
+ int drag_area_margin_begin = 0;
+ int drag_area_margin_end = 0;
+ int drag_area_offset = 0;
int split_offset = 0;
- int middle_sep = 0;
+ int computed_split_offset = 0;
bool vertical = false;
bool collapsed = false;
DraggerVisibility dragger_visibility = DRAGGER_VISIBLE;
+ bool dragging_enabled = true;
SplitContainerDragger *dragging_area_control = nullptr;
@@ -77,10 +84,13 @@ private:
Ref<Texture2D> grabber_icon;
Ref<Texture2D> grabber_icon_h;
Ref<Texture2D> grabber_icon_v;
+ float base_scale = 1.0;
+ Ref<StyleBox> split_bar_background;
} theme_cache;
Ref<Texture2D> _get_grabber_icon() const;
- void _compute_middle_sep(bool p_clamp);
+ void _compute_split_offset(bool p_clamp);
+ int _get_separation() const;
void _resort();
Control *_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const;
@@ -105,11 +115,28 @@ public:
void set_vertical(bool p_vertical);
bool is_vertical() const;
+ void set_dragging_enabled(bool p_enabled);
+ bool is_dragging_enabled() const;
+
virtual Size2 get_minimum_size() const override;
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
+ void set_drag_area_margin_begin(int p_margin);
+ int get_drag_area_margin_begin() const;
+
+ void set_drag_area_margin_end(int p_margin);
+ int get_drag_area_margin_end() const;
+
+ void set_drag_area_offset(int p_offset);
+ int get_drag_area_offset() const;
+
+ void set_show_drag_area_enabled(bool p_enabled);
+ bool is_show_drag_area_enabled() const;
+
+ Control *get_drag_area_control() { return dragging_area_control; }
+
SplitContainer(bool p_vertical = false);
};
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 3e0d6adf42..6cc7ec3bd5 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -354,6 +354,8 @@ void TabBar::_notification(int p_what) {
if (scroll_to_selected) {
ensure_tab_visible(current);
}
+ // Set initialized even if no tabs were set.
+ initialized = true;
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
@@ -655,10 +657,10 @@ void TabBar::set_tab_count(int p_count) {
}
if (!initialized) {
- if (queued_current != current) {
- current = queued_current;
- }
initialized = true;
+ if (queued_current != CURRENT_TAB_UNINITIALIZED && queued_current != current) {
+ set_current_tab(queued_current);
+ }
}
queue_redraw();
@@ -740,6 +742,13 @@ bool TabBar::select_next_available() {
return false;
}
+void TabBar::set_tab_offset(int p_offset) {
+ ERR_FAIL_INDEX(p_offset, tabs.size());
+ offset = p_offset;
+ _update_cache();
+ queue_redraw();
+}
+
int TabBar::get_tab_offset() const {
return offset;
}
@@ -1250,6 +1259,7 @@ Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_poin
}
Label *label = memnew(Label(get_tab_title(tab_over)));
+ label->set_auto_translate_mode(get_auto_translate_mode()); // Reflect how the title is displayed.
drag_preview->add_child(label);
set_drag_preview(drag_preview);
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index d62b39ae16..f5ae75de51 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -117,8 +117,9 @@ private:
bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
+ static const int CURRENT_TAB_UNINITIALIZED = -2;
bool initialized = false;
- int queued_current = -1;
+ int queued_current = CURRENT_TAB_UNINITIALIZED;
const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 0.5;
const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 20;
@@ -249,6 +250,7 @@ public:
bool select_previous_available();
bool select_next_available();
+ void set_tab_offset(int p_offset);
int get_tab_offset() const;
bool get_offset_buttons_visible() const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 23b99d1e53..33e72428cf 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -190,7 +190,7 @@ void TabContainer::_notification(int p_what) {
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
- if (!is_visible() || setup_current_tab > -2) {
+ if (!is_visible()) {
return;
}
@@ -200,7 +200,7 @@ void TabContainer::_notification(int p_what) {
// beat it to the punch and make sure that the correct node is the only one visible first.
// Otherwise, it can prevent a tab change done right before this container was made visible.
Vector<Control *> controls = _get_tab_controls();
- int current = get_current_tab();
+ int current = setup_current_tab > -2 ? setup_current_tab : get_current_tab();
for (int i = 0; i < controls.size(); i++) {
controls[i]->set_visible(i == current);
}
@@ -267,10 +267,14 @@ void TabContainer::_repaint() {
Vector<Control *> controls = _get_tab_controls();
int current = get_current_tab();
+ // Move the TabBar to the top or bottom.
+ // Don't change the left and right offsets since the TabBar will resize and may change tab offset.
if (tabs_position == POSITION_BOTTOM) {
- tab_bar->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
+ tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 1.0, 0.0);
+ tab_bar->set_anchor_and_offset(SIDE_TOP, 1.0, -_get_tab_height());
} else {
- tab_bar->set_anchors_and_offsets_preset(PRESET_TOP_WIDE);
+ tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 0.0, _get_tab_height());
+ tab_bar->set_anchor_and_offset(SIDE_TOP, 0.0, 0.0);
}
updating_visibility = true;
@@ -299,7 +303,6 @@ void TabContainer::_repaint() {
}
updating_visibility = false;
- _update_margins();
update_minimum_size();
}
@@ -909,7 +912,7 @@ Size2 TabContainer::get_minimum_size() const {
for (int i = 0; i < controls.size(); i++) {
Control *c = controls[i];
- if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) {
+ if (!c->is_visible() && !use_hidden_tabs_for_min_size) {
continue;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index d7799588ea..6b5ff23436 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1265,7 +1265,7 @@ void TextEdit::_notification(int p_what) {
}
if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word
- if (is_ascii_alphabet_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') {
+ if (is_unicode_identifier_start(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') {
Color highlight_underline_color = !editable ? theme_cache.font_readonly_color : theme_cache.font_color;
int lookup_symbol_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
int lookup_symbol_word_len = lookup_symbol_word.length();
@@ -3034,6 +3034,7 @@ Variant TextEdit::get_drag_data(const Point2 &p_point) {
if (has_selection() && selection_drag_attempt) {
String t = get_selected_text();
Label *l = memnew(Label);
+ l->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Don't translate user input.
l->set_text(t);
set_drag_preview(l);
return t;
@@ -3366,6 +3367,14 @@ bool TextEdit::is_middle_mouse_paste_enabled() const {
return middle_mouse_paste_enabled;
}
+void TextEdit::set_empty_selection_clipboard_enabled(bool p_enabled) {
+ empty_selection_clipboard_enabled = p_enabled;
+}
+
+bool TextEdit::is_empty_selection_clipboard_enabled() const {
+ return empty_selection_clipboard_enabled;
+}
+
// Text manipulation
void TextEdit::clear() {
setting_text = true;
@@ -6568,6 +6577,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_middle_mouse_paste_enabled", "enabled"), &TextEdit::set_middle_mouse_paste_enabled);
ClassDB::bind_method(D_METHOD("is_middle_mouse_paste_enabled"), &TextEdit::is_middle_mouse_paste_enabled);
+ ClassDB::bind_method(D_METHOD("set_empty_selection_clipboard_enabled", "enabled"), &TextEdit::set_empty_selection_clipboard_enabled);
+ ClassDB::bind_method(D_METHOD("is_empty_selection_clipboard_enabled"), &TextEdit::is_empty_selection_clipboard_enabled);
+
// Text manipulation
ClassDB::bind_method(D_METHOD("clear"), &TextEdit::clear);
@@ -6961,6 +6973,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "empty_selection_clipboard_enabled"), "set_empty_selection_clipboard_enabled", "is_empty_selection_clipboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_wrapped_lines"), "set_indent_wrapped_lines", "is_indent_wrapped_lines");
@@ -7215,6 +7228,10 @@ void TextEdit::_cut_internal(int p_caret) {
return;
}
+ if (!empty_selection_clipboard_enabled) {
+ return;
+ }
+
// Remove full lines.
begin_complex_operation();
begin_multicaret_edit();
@@ -7245,6 +7262,10 @@ void TextEdit::_copy_internal(int p_caret) {
return;
}
+ if (!empty_selection_clipboard_enabled) {
+ return;
+ }
+
// Copy full lines.
StringBuilder clipboard;
Vector<Point2i> line_ranges;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index c5f838020b..94b105d486 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -319,6 +319,7 @@ private:
bool shortcut_keys_enabled = true;
bool virtual_keyboard_enabled = true;
bool middle_mouse_paste_enabled = true;
+ bool empty_selection_clipboard_enabled = true;
// Overridable actions.
String cut_copy_line = "";
@@ -770,6 +771,9 @@ public:
void set_middle_mouse_paste_enabled(bool p_enabled);
bool is_middle_mouse_paste_enabled() const;
+ void set_empty_selection_clipboard_enabled(bool p_enabled);
+ bool is_empty_selection_clipboard_enabled() const;
+
// Text manipulation
void clear();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e4f52ee8ee..646cd9c70e 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -185,6 +185,26 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
return cells[p_column].mode;
}
+/* auto translate mode */
+void TreeItem::set_auto_translate_mode(int p_column, Node::AutoTranslateMode p_mode) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+
+ if (cells[p_column].auto_translate_mode == p_mode) {
+ return;
+ }
+
+ cells.write[p_column].auto_translate_mode = p_mode;
+ cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
+ _changed_notify(p_column);
+}
+
+Node::AutoTranslateMode TreeItem::get_auto_translate_mode(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), Node::AUTO_TRANSLATE_MODE_INHERIT);
+ return cells[p_column].auto_translate_mode;
+}
+
/* multiline editable */
void TreeItem::set_edit_multiline(int p_column, bool p_multiline) {
ERR_FAIL_INDEX(p_column, cells.size());
@@ -247,6 +267,24 @@ void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
_propagate_check_through_parents(p_column, p_emit_signal);
}
+String TreeItem::atr(int p_column, const String &p_text) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), tree->atr(p_text));
+
+ switch (cells[p_column].auto_translate_mode) {
+ case Node::AUTO_TRANSLATE_MODE_INHERIT: {
+ return tree->atr(p_text);
+ } break;
+ case Node::AUTO_TRANSLATE_MODE_ALWAYS: {
+ return tree->tr(p_text);
+ } break;
+ case Node::AUTO_TRANSLATE_MODE_DISABLED: {
+ return p_text;
+ } break;
+ }
+
+ ERR_FAIL_V_MSG(tree->atr(p_text), "Unexpected auto translate mode: " + itos(cells[p_column].auto_translate_mode));
+}
+
void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) {
TreeItem *current = get_first_child();
while (current) {
@@ -323,7 +361,7 @@ void TreeItem::set_text(int p_column, String p_text) {
} else {
// Don't auto translate if it's in string mode and editable, as the text can be changed to anything by the user.
if (tree && (!cells[p_column].editable || cells[p_column].mode != TreeItem::CELL_MODE_STRING)) {
- cells.write[p_column].xl_text = tree->atr(p_text);
+ cells.write[p_column].xl_text = atr(p_column, p_text);
} else {
cells.write[p_column].xl_text = p_text;
}
@@ -1621,6 +1659,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_mode", "column", "mode"), &TreeItem::set_cell_mode);
ClassDB::bind_method(D_METHOD("get_cell_mode", "column"), &TreeItem::get_cell_mode);
+ ClassDB::bind_method(D_METHOD("set_auto_translate_mode", "column", "mode"), &TreeItem::set_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("get_auto_translate_mode", "column"), &TreeItem::get_auto_translate_mode);
+
ClassDB::bind_method(D_METHOD("set_edit_multiline", "column", "multiline"), &TreeItem::set_edit_multiline);
ClassDB::bind_method(D_METHOD("is_edit_multiline", "column"), &TreeItem::is_edit_multiline);
@@ -2009,7 +2050,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
int option = (int)p_item->cells[p_col].val;
- valtext = atr(ETR("(Other)"));
+ valtext = p_item->atr(p_col, ETR("(Other)"));
Vector<String> strings = p_item->cells[p_col].text.split(",");
for (int j = 0; j < strings.size(); j++) {
int value = j;
@@ -2017,7 +2058,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
value = strings[j].get_slicec(':', 1).to_int();
}
if (option == value) {
- valtext = atr(strings[j].get_slicec(':', 0));
+ valtext = p_item->atr(p_col, strings[j].get_slicec(':', 0));
break;
}
}
@@ -2028,7 +2069,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
// Don't auto translate if it's in string mode and editable, as the text can be changed to anything by the user.
if (!p_item->cells[p_col].editable || p_item->cells[p_col].mode != TreeItem::CELL_MODE_STRING) {
- p_item->cells.write[p_col].xl_text = atr(p_item->cells[p_col].text);
+ p_item->cells.write[p_col].xl_text = p_item->atr(p_col, p_item->cells[p_col].text);
} else {
p_item->cells.write[p_col].xl_text = p_item->cells[p_col].text;
}
@@ -3993,25 +4034,25 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
} break;
case MouseButton::WHEEL_UP: {
- if (_scroll(false, -mb->get_factor() / 8)) {
+ if (_scroll(mb->is_shift_pressed(), -mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_DOWN: {
- if (_scroll(false, mb->get_factor() / 8)) {
+ if (_scroll(mb->is_shift_pressed(), mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_LEFT: {
- if (_scroll(true, -mb->get_factor() / 8)) {
+ if (_scroll(!mb->is_shift_pressed(), -mb->get_factor() / 8)) {
accept_event();
}
} break;
case MouseButton::WHEEL_RIGHT: {
- if (_scroll(true, mb->get_factor() / 8)) {
+ if (_scroll(!mb->is_shift_pressed(), mb->get_factor() / 8)) {
accept_event();
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 17ea31a733..86efdfec52 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -64,6 +64,7 @@ private:
Rect2i icon_region;
String text;
String xl_text;
+ Node::AutoTranslateMode auto_translate_mode = Node::AUTO_TRANSLATE_MODE_INHERIT;
bool edit_multiline = false;
String suffix;
Ref<TextParagraph> text_buf;
@@ -210,6 +211,10 @@ public:
void set_cell_mode(int p_column, TreeCellMode p_mode);
TreeCellMode get_cell_mode(int p_column) const;
+ /* auto translate mode */
+ void set_auto_translate_mode(int p_column, Node::AutoTranslateMode p_mode);
+ Node::AutoTranslateMode get_auto_translate_mode(int p_column) const;
+
/* multiline editable */
void set_edit_multiline(int p_column, bool p_multiline);
bool is_edit_multiline(int p_column) const;
@@ -222,6 +227,8 @@ public:
void propagate_check(int p_column, bool p_emit_signal = true);
+ String atr(int p_column, const String &p_text) const;
+
private:
// Check helpers.
void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
diff --git a/scene/main/SCsub b/scene/main/SCsub
index fc61250247..374dc2119d 100644
--- a/scene/main/SCsub
+++ b/scene/main/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 3469b806a6..8526611093 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -49,7 +49,8 @@ Error HTTPRequest::_parse_url(const String &p_url) {
redirections = 0;
String scheme;
- Error err = p_url.parse_url(scheme, url, port, request_string);
+ String fragment;
+ Error err = p_url.parse_url(scheme, url, port, request_string, fragment);
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
if (scheme == "https://") {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 858fc2246b..d921cc5b67 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -111,6 +111,7 @@ void Node::_notification(int p_notification) {
data.auto_translate_mode = AUTO_TRANSLATE_MODE_ALWAYS;
}
data.is_auto_translate_dirty = true;
+ data.is_translation_domain_dirty = true;
#ifdef TOOLS_ENABLED
// Don't translate UI elements when they're being edited.
@@ -1320,6 +1321,51 @@ bool Node::can_auto_translate() const {
return data.is_auto_translating;
}
+StringName Node::get_translation_domain() const {
+ ERR_READ_THREAD_GUARD_V(StringName());
+
+ if (data.is_translation_domain_inherited && data.is_translation_domain_dirty) {
+ const_cast<Node *>(this)->_translation_domain = data.parent ? data.parent->get_translation_domain() : StringName();
+ data.is_translation_domain_dirty = false;
+ }
+ return _translation_domain;
+}
+
+void Node::set_translation_domain(const StringName &p_domain) {
+ ERR_THREAD_GUARD
+
+ if (!data.is_translation_domain_inherited && _translation_domain == p_domain) {
+ return;
+ }
+
+ _translation_domain = p_domain;
+ data.is_translation_domain_inherited = false;
+ data.is_translation_domain_dirty = false;
+ _propagate_translation_domain_dirty();
+}
+
+void Node::set_translation_domain_inherited() {
+ ERR_THREAD_GUARD
+
+ if (data.is_translation_domain_inherited) {
+ return;
+ }
+ data.is_translation_domain_inherited = true;
+ data.is_translation_domain_dirty = true;
+ _propagate_translation_domain_dirty();
+}
+
+void Node::_propagate_translation_domain_dirty() {
+ for (KeyValue<StringName, Node *> &K : data.children) {
+ Node *child = K.value;
+ if (child->data.is_translation_domain_inherited) {
+ child->data.is_translation_domain_dirty = true;
+ child->_propagate_translation_domain_dirty();
+ }
+ }
+ notification(NOTIFICATION_TRANSLATION_CHANGED);
+}
+
StringName Node::get_name() const {
return data.name;
}
@@ -3610,6 +3656,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_auto_translate_mode", "mode"), &Node::set_auto_translate_mode);
ClassDB::bind_method(D_METHOD("get_auto_translate_mode"), &Node::get_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("set_translation_domain_inherited"), &Node::set_translation_domain_inherited);
ClassDB::bind_method(D_METHOD("get_window"), &Node::get_window);
ClassDB::bind_method(D_METHOD("get_last_exclusive_window"), &Node::get_last_exclusive_window);
@@ -3972,4 +4019,9 @@ bool Node::is_connected(const StringName &p_signal, const Callable &p_callable)
return Object::is_connected(p_signal, p_callable);
}
+bool Node::has_connections(const StringName &p_signal) const {
+ ERR_THREAD_GUARD_V(false);
+ return Object::has_connections(p_signal);
+}
+
#endif
diff --git a/scene/main/node.h b/scene/main/node.h
index dc65513fca..799478fa35 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -255,6 +255,9 @@ private:
mutable bool is_auto_translating = true;
mutable bool is_auto_translate_dirty = true;
+ mutable bool is_translation_domain_inherited = true;
+ mutable bool is_translation_domain_dirty = true;
+
mutable NodePath *path_cache = nullptr;
} data;
@@ -281,6 +284,7 @@ private:
void _propagate_physics_interpolation_reset_requested(bool p_requested);
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
void _propagate_groups_dirty();
+ void _propagate_translation_domain_dirty();
Array _get_node_and_resource(const NodePath &p_path);
void _duplicate_properties(const Node *p_root, const Node *p_original, Node *p_copy, int p_flags) const;
@@ -735,8 +739,17 @@ public:
AutoTranslateMode get_auto_translate_mode() const;
bool can_auto_translate() const;
- _FORCE_INLINE_ String atr(const String p_message, const StringName p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; }
- _FORCE_INLINE_ String atr_n(const String p_message, const StringName &p_message_plural, int p_n, const StringName p_context = "") const { return can_auto_translate() ? tr_n(p_message, p_message_plural, p_n, p_context) : p_message; }
+ virtual StringName get_translation_domain() const override;
+ virtual void set_translation_domain(const StringName &p_domain) override;
+ void set_translation_domain_inherited();
+
+ _FORCE_INLINE_ String atr(const String &p_message, const StringName &p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; }
+ _FORCE_INLINE_ String atr_n(const String &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const {
+ if (can_auto_translate()) {
+ return tr_n(p_message, p_message_plural, p_n, p_context);
+ }
+ return p_n == 1 ? p_message : String(p_message_plural);
+ }
/* THREADING */
@@ -789,6 +802,7 @@ public:
virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override;
virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override;
virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override;
+ virtual bool has_connections(const StringName &p_signal) const override;
#endif
Node();
~Node();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 106130872d..71d91b970e 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -302,11 +302,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
+ Node *node = gr_nodes[i];
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ node->callp(p_function, p_args, p_argcount, ce);
+ if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) {
+ ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce)));
+ }
} else {
- MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount);
}
}
@@ -316,11 +320,15 @@ void SceneTree::call_group_flagsp(uint32_t p_call_flags, const StringName &p_gro
continue;
}
+ Node *node = gr_nodes[i];
if (!(p_call_flags & GROUP_CALL_DEFERRED)) {
Callable::CallError ce;
- gr_nodes[i]->callp(p_function, p_args, p_argcount, ce);
+ node->callp(p_function, p_args, p_argcount, ce);
+ if (unlikely(ce.error != Callable::CallError::CALL_OK && ce.error != Callable::CallError::CALL_ERROR_INVALID_METHOD)) {
+ ERR_PRINT(vformat("Error calling group method on node \"%s\": %s.", node->get_name(), Variant::get_callable_error_text(Callable(node, p_function), p_args, p_argcount, ce)));
+ }
} else {
- MessageQueue::get_singleton()->push_callp(gr_nodes[i], p_function, p_args, p_argcount);
+ MessageQueue::get_singleton()->push_callp(node, p_function, p_args, p_argcount);
}
}
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index a4e27a3d16..169a5dcb01 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -111,6 +111,9 @@ void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
if (get_local_scene() && !path.is_empty()) {
setup_local_to_scene();
} else {
+ if (path.is_empty()) {
+ vp_changed = false;
+ }
emit_changed();
}
}
@@ -121,9 +124,7 @@ NodePath ViewportTexture::get_viewport_path_in_scene() const {
int ViewportTexture::get_width() const {
if (!vp) {
- if (!vp_pending) {
- ERR_PRINT("Viewport Texture must be set to use it.");
- }
+ _err_print_viewport_not_set();
return 0;
}
return vp->size.width;
@@ -131,9 +132,7 @@ int ViewportTexture::get_width() const {
int ViewportTexture::get_height() const {
if (!vp) {
- if (!vp_pending) {
- ERR_PRINT("Viewport Texture must be set to use it.");
- }
+ _err_print_viewport_not_set();
return 0;
}
return vp->size.height;
@@ -141,9 +140,7 @@ int ViewportTexture::get_height() const {
Size2 ViewportTexture::get_size() const {
if (!vp) {
- if (!vp_pending) {
- ERR_PRINT("Viewport Texture must be set to use it.");
- }
+ _err_print_viewport_not_set();
return Size2();
}
return vp->size;
@@ -163,14 +160,18 @@ bool ViewportTexture::has_alpha() const {
Ref<Image> ViewportTexture::get_image() const {
if (!vp) {
- if (!vp_pending) {
- ERR_PRINT("Viewport Texture must be set to use it.");
- }
+ _err_print_viewport_not_set();
return Ref<Image>();
}
return RS::get_singleton()->texture_2d_get(vp->texture_rid);
}
+void ViewportTexture::_err_print_viewport_not_set() const {
+ if (!vp_pending && !vp_changed) {
+ ERR_PRINT("Viewport Texture must be set to use it.");
+ }
+}
+
void ViewportTexture::_setup_local_to_scene(const Node *p_loc_scene) {
// Always reset this, even if this call fails with an error.
vp_pending = false;
@@ -1399,7 +1400,7 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont
String tooltip;
while (p_control) {
- tooltip = p_control->atr(p_control->get_tooltip(pos));
+ tooltip = p_control->get_tooltip(pos);
// Temporary solution for PopupMenus.
PopupMenu *menu = Object::cast_to<PopupMenu>(this);
@@ -1482,6 +1483,7 @@ void Viewport::_gui_show_tooltip() {
}
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ base_tooltip->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode());
panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
@@ -4670,6 +4672,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
+ ClassDB::bind_method(D_METHOD("get_audio_listener_2d"), &Viewport::get_audio_listener_2d);
ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d);
#ifndef _3D_DISABLED
@@ -4680,6 +4683,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d);
ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d);
+ ClassDB::bind_method(D_METHOD("get_audio_listener_3d"), &Viewport::get_audio_listener_3d);
ClassDB::bind_method(D_METHOD("get_camera_3d"), &Viewport::get_camera_3d);
ClassDB::bind_method(D_METHOD("set_as_audio_listener_3d", "enable"), &Viewport::set_as_audio_listener_3d);
ClassDB::bind_method(D_METHOD("is_audio_listener_3d"), &Viewport::is_audio_listener_3d);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index edacee2d88..a18dc1f6f0 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -63,6 +63,7 @@ class ViewportTexture : public Texture2D {
bool vp_changed = false;
void _setup_local_to_scene(const Node *p_loc_scene);
+ void _err_print_viewport_not_set() const;
mutable RID proxy_ph;
mutable RID proxy;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 76678e609a..6b1ce2b4ca 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -117,6 +117,7 @@
#include "scene/resources/compressed_texture.h"
#include "scene/resources/curve_texture.h"
#include "scene/resources/environment.h"
+#include "scene/resources/external_texture.h"
#include "scene/resources/font.h"
#include "scene/resources/gradient.h"
#include "scene/resources/gradient_texture.h"
@@ -882,7 +883,6 @@ void register_scene_types() {
GDREGISTER_CLASS(ProceduralSkyMaterial);
GDREGISTER_CLASS(PanoramaSkyMaterial);
GDREGISTER_CLASS(PhysicalSkyMaterial);
- SceneTree::add_idle_callback(BaseMaterial3D::flush_changes);
BaseMaterial3D::init_shaders();
GDREGISTER_CLASS(MeshLibrary);
@@ -926,6 +926,7 @@ void register_scene_types() {
GDREGISTER_CLASS(GradientTexture2D);
GDREGISTER_CLASS(AnimatedTexture);
GDREGISTER_CLASS(CameraTexture);
+ GDREGISTER_CLASS(ExternalTexture);
GDREGISTER_VIRTUAL_CLASS(TextureLayered);
GDREGISTER_ABSTRACT_CLASS(ImageTextureLayered);
GDREGISTER_VIRTUAL_CLASS(Texture3D);
diff --git a/scene/resources/2d/SCsub b/scene/resources/2d/SCsub
index fdf20e0bde..408aa3cf7e 100644
--- a/scene/resources/2d/SCsub
+++ b/scene/resources/2d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index 931495d020..15e1a16359 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -571,25 +571,25 @@ public:
// Not exposed.
virtual void set_tile_set(const TileSet *p_tile_set);
TileSet *get_tile_set() const;
- virtual void notify_tile_data_properties_should_change(){};
- virtual void add_occlusion_layer(int p_index){};
- virtual void move_occlusion_layer(int p_from_index, int p_to_pos){};
- virtual void remove_occlusion_layer(int p_index){};
- virtual void add_physics_layer(int p_index){};
- virtual void move_physics_layer(int p_from_index, int p_to_pos){};
- virtual void remove_physics_layer(int p_index){};
- virtual void add_terrain_set(int p_index){};
- virtual void move_terrain_set(int p_from_index, int p_to_pos){};
- virtual void remove_terrain_set(int p_index){};
- virtual void add_terrain(int p_terrain_set, int p_index){};
- virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos){};
- virtual void remove_terrain(int p_terrain_set, int p_index){};
- virtual void add_navigation_layer(int p_index){};
- virtual void move_navigation_layer(int p_from_index, int p_to_pos){};
- virtual void remove_navigation_layer(int p_index){};
- virtual void add_custom_data_layer(int p_index){};
- virtual void move_custom_data_layer(int p_from_index, int p_to_pos){};
- virtual void remove_custom_data_layer(int p_index){};
+ virtual void notify_tile_data_properties_should_change() {}
+ virtual void add_occlusion_layer(int p_index) {}
+ virtual void move_occlusion_layer(int p_from_index, int p_to_pos) {}
+ virtual void remove_occlusion_layer(int p_index) {}
+ virtual void add_physics_layer(int p_index) {}
+ virtual void move_physics_layer(int p_from_index, int p_to_pos) {}
+ virtual void remove_physics_layer(int p_index) {}
+ virtual void add_terrain_set(int p_index) {}
+ virtual void move_terrain_set(int p_from_index, int p_to_pos) {}
+ virtual void remove_terrain_set(int p_index) {}
+ virtual void add_terrain(int p_terrain_set, int p_index) {}
+ virtual void move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {}
+ virtual void remove_terrain(int p_terrain_set, int p_index) {}
+ virtual void add_navigation_layer(int p_index) {}
+ virtual void move_navigation_layer(int p_from_index, int p_to_pos) {}
+ virtual void remove_navigation_layer(int p_index) {}
+ virtual void add_custom_data_layer(int p_index) {}
+ virtual void move_custom_data_layer(int p_from_index, int p_to_pos) {}
+ virtual void remove_custom_data_layer(int p_index) {}
virtual void reset_state() override;
// Tiles.
diff --git a/scene/resources/3d/SCsub b/scene/resources/3d/SCsub
index fdf20e0bde..408aa3cf7e 100644
--- a/scene/resources/3d/SCsub
+++ b/scene/resources/3d/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/3d/fog_material.cpp b/scene/resources/3d/fog_material.cpp
index 92246b50db..6c6e98b50d 100644
--- a/scene/resources/3d/fog_material.cpp
+++ b/scene/resources/3d/fog_material.cpp
@@ -168,6 +168,8 @@ void fog() {
}
FogMaterial::FogMaterial() {
+ _set_material(RS::get_singleton()->material_create());
+
set_density(1.0);
set_albedo(Color(1, 1, 1, 1));
set_emission(Color(0, 0, 0, 1));
diff --git a/scene/resources/3d/primitive_meshes.h b/scene/resources/3d/primitive_meshes.h
index fc2489923a..85f46a482a 100644
--- a/scene/resources/3d/primitive_meshes.h
+++ b/scene/resources/3d/primitive_meshes.h
@@ -77,7 +77,7 @@ protected:
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(){};
+ virtual void _update_lightmap_size() {}
void _on_settings_changed();
@@ -541,7 +541,7 @@ private:
Vector2 point;
bool sharp = false;
- ContourPoint(){};
+ ContourPoint() {}
ContourPoint(const Vector2 &p_pt, bool p_sharp) {
point = p_pt;
sharp = p_sharp;
@@ -551,7 +551,7 @@ private:
struct ContourInfo {
real_t length = 0.0;
bool ccw = true;
- ContourInfo(){};
+ ContourInfo() {}
ContourInfo(real_t p_len, bool p_ccw) {
length = p_len;
ccw = p_ccw;
diff --git a/scene/resources/3d/sky_material.cpp b/scene/resources/3d/sky_material.cpp
index c470db5d7f..10ef516f7a 100644
--- a/scene/resources/3d/sky_material.cpp
+++ b/scene/resources/3d/sky_material.cpp
@@ -357,6 +357,7 @@ void sky() {
}
ProceduralSkyMaterial::ProceduralSkyMaterial() {
+ _set_material(RS::get_singleton()->material_create());
set_sky_top_color(Color(0.385, 0.454, 0.55));
set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708));
set_sky_curve(0.15);
@@ -486,6 +487,7 @@ void sky() {
}
PanoramaSkyMaterial::PanoramaSkyMaterial() {
+ _set_material(RS::get_singleton()->material_create());
set_energy_multiplier(1.0);
}
@@ -785,6 +787,7 @@ void sky() {
}
PhysicalSkyMaterial::PhysicalSkyMaterial() {
+ _set_material(RS::get_singleton()->material_create());
set_rayleigh_coefficient(2.0);
set_rayleigh_color(Color(0.3, 0.405, 0.6));
set_mie_coefficient(0.005);
diff --git a/scene/resources/SCsub b/scene/resources/SCsub
index 2b6aa88d2c..46f6251b91 100644
--- a/scene/resources/SCsub
+++ b/scene/resources/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index a2ed6af23c..57a4e35f7a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -63,6 +63,23 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
compression.enabled = true;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers = p_value;
+ for (const Dictionary marker : markers) {
+ ERR_FAIL_COND_V(!marker.has("name"), false);
+ ERR_FAIL_COND_V(!marker.has("time"), false);
+ StringName marker_name = marker["name"];
+ double time = marker["time"];
+ _marker_insert(time, marker_names, MarkerKey(time, marker_name));
+ marker_times.insert(marker_name, time);
+ Color color = Color(1, 1, 1);
+ if (marker.has("color")) {
+ color = marker["color"];
+ }
+ marker_colors.insert(marker_name, color);
+ }
+
+ return true;
} else if (prop_name.begins_with("tracks/")) {
int track = prop_name.get_slicec('/', 1).to_int();
String what = prop_name.get_slicec('/', 2);
@@ -321,8 +338,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
Vector<real_t> times = d["times"];
Vector<real_t> values = d["points"];
#ifdef TOOLS_ENABLED
- ERR_FAIL_COND_V(!d.has("handle_modes"), false);
- Vector<int> handle_modes = d["handle_modes"];
+ Vector<int> handle_modes;
+ if (d.has("handle_modes")) {
+ handle_modes = d["handle_modes"];
+ } else {
+ handle_modes.resize_zeroed(times.size());
+ }
#endif // TOOLS_ENABLED
ERR_FAIL_COND_V(times.size() * 5 != values.size(), false);
@@ -466,6 +487,18 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = comp;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers;
+
+ for (HashMap<StringName, double>::ConstIterator E = marker_times.begin(); E; ++E) {
+ Dictionary d;
+ d["name"] = E->key;
+ d["time"] = E->value;
+ d["color"] = marker_colors[E->key];
+ markers.push_back(d);
+ }
+
+ r_ret = markers;
} else if (prop_name == "length") {
r_ret = length;
} else if (prop_name == "loop_mode") {
@@ -835,6 +868,7 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
if (compression.enabled) {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "markers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
for (int i = 0; i < tracks.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
@@ -1083,6 +1117,27 @@ int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
return -1;
}
+int Animation::_marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value) {
+ int idx = p_keys.size();
+
+ while (true) {
+ // Condition for replacement.
+ if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
+ p_keys.write[idx - 1] = p_value;
+ return idx - 1;
+
+ // Condition for insert.
+ } else if (idx == 0 || p_keys[idx - 1].time < p_time) {
+ p_keys.insert(idx, p_value);
+ return idx;
+ }
+
+ idx--;
+ }
+
+ return -1;
+}
+
template <typename T>
void Animation::_clear(T &p_keys) {
p_keys.clear();
@@ -3159,6 +3214,90 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
}
+void Animation::add_marker(const StringName &p_name, double p_time) {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(p_time, marker_names[idx].time)) {
+ marker_times.erase(marker_names[idx].name);
+ marker_colors.erase(marker_names[idx].name);
+ marker_names.write[idx].name = p_name;
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ } else {
+ _marker_insert(p_time, marker_names, MarkerKey(p_time, p_name));
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ }
+}
+
+void Animation::remove_marker(const StringName &p_name) {
+ HashMap<StringName, double>::Iterator E = marker_times.find(p_name);
+ ERR_FAIL_COND(!E);
+ int idx = _find(marker_names, E->value);
+ bool success = idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, E->value);
+ ERR_FAIL_COND(!success);
+ marker_names.remove_at(idx);
+ marker_times.remove(E);
+ marker_colors.erase(p_name);
+}
+
+bool Animation::has_marker(const StringName &p_name) const {
+ return marker_times.has(p_name);
+}
+
+StringName Animation::get_marker_at_time(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, p_time)) {
+ return marker_names[idx].name;
+ }
+
+ return StringName();
+}
+
+StringName Animation::get_next_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= -1 && idx < marker_names.size() - 1) {
+ // _find ensures that the time at idx is always the closest time to p_time that is also smaller to it.
+ // So we add 1 to get the next marker.
+ return marker_names[idx + 1].name;
+ }
+ return StringName();
+}
+
+StringName Animation::get_prev_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size()) {
+ return marker_names[idx].name;
+ }
+ return StringName();
+}
+
+double Animation::get_marker_time(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_times.has(p_name), -1);
+ return marker_times.get(p_name);
+}
+
+PackedStringArray Animation::get_marker_names() const {
+ PackedStringArray names;
+ // We iterate on marker_names so the result is sorted by time.
+ for (const MarkerKey &marker_name : marker_names) {
+ names.push_back(marker_name.name);
+ }
+ return names;
+}
+
+Color Animation::get_marker_color(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_colors.has(p_name), Color());
+ return marker_colors[p_name];
+}
+
+void Animation::set_marker_color(const StringName &p_name, const Color &p_color) {
+ marker_colors[p_name] = p_color;
+}
+
Vector<Variant> Animation::method_track_get_params(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector<Variant>());
Track *t = tracks[p_track];
@@ -3890,6 +4029,17 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation);
+ ClassDB::bind_method(D_METHOD("add_marker", "name", "time"), &Animation::add_marker);
+ ClassDB::bind_method(D_METHOD("remove_marker", "name"), &Animation::remove_marker);
+ ClassDB::bind_method(D_METHOD("has_marker", "name"), &Animation::has_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_at_time", "time"), &Animation::get_marker_at_time);
+ ClassDB::bind_method(D_METHOD("get_next_marker", "time"), &Animation::get_next_marker);
+ ClassDB::bind_method(D_METHOD("get_prev_marker", "time"), &Animation::get_prev_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_time", "name"), &Animation::get_marker_time);
+ ClassDB::bind_method(D_METHOD("get_marker_names"), &Animation::get_marker_names);
+ ClassDB::bind_method(D_METHOD("get_marker_color", "name"), &Animation::get_marker_color);
+ ClassDB::bind_method(D_METHOD("set_marker_color", "name", "color"), &Animation::set_marker_color);
+
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@@ -3902,6 +4052,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
+ ClassDB::bind_method(D_METHOD("optimize", "allowed_velocity_err", "allowed_angular_err", "precision"), &Animation::optimize, DEFVAL(0.01), DEFVAL(0.01), DEFVAL(3));
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("is_capture_included"), &Animation::is_capture_included);
@@ -4804,9 +4955,9 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol
continue; // This track is exhausted (all keys were added already), don't consider.
}
}
-
- uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
-
+ double key_time = track_get_key_time(uncomp_track, time_tracks[i].key_index);
+ double result = key_time / frame_len;
+ uint32_t key_frame = Math::fast_ftoi(result);
if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
start_frame = true;
best_frame = base_page_frame;
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 0c29790ea4..618dc9ca17 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -237,6 +237,20 @@ private:
}
};
+ /* Marker */
+
+ struct MarkerKey {
+ double time;
+ StringName name;
+ MarkerKey(double p_time, const StringName &p_name) :
+ time(p_time), name(p_name) {}
+ MarkerKey() = default;
+ };
+
+ Vector<MarkerKey> marker_names; // time -> name
+ HashMap<StringName, double> marker_times; // name -> time
+ HashMap<StringName, Color> marker_colors; // name -> color
+
Vector<Track *> tracks;
template <typename T>
@@ -245,6 +259,8 @@ private:
template <typename T, typename V>
int _insert(double p_time, T &p_keys, const V &p_value);
+ int _marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value);
+
template <typename K>
inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false, bool p_limit = false) const;
@@ -501,6 +517,17 @@ public:
void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const;
+ void add_marker(const StringName &p_name, double p_time);
+ void remove_marker(const StringName &p_name);
+ bool has_marker(const StringName &p_name) const;
+ StringName get_marker_at_time(double p_time) const;
+ StringName get_next_marker(double p_time) const;
+ StringName get_prev_marker(double p_time) const;
+ double get_marker_time(const StringName &p_time) const;
+ PackedStringArray get_marker_names() const;
+ Color get_marker_color(const StringName &p_name) const;
+ void set_marker_color(const StringName &p_name, const Color &p_color);
+
void set_length(real_t p_length);
real_t get_length() const;
diff --git a/scene/resources/camera_attributes.h b/scene/resources/camera_attributes.h
index 0f819134e2..de57b0ce8f 100644
--- a/scene/resources/camera_attributes.h
+++ b/scene/resources/camera_attributes.h
@@ -53,7 +53,7 @@ protected:
float auto_exposure_max = 64.0;
float auto_exposure_speed = 0.5;
float auto_exposure_scale = 0.4;
- virtual void _update_auto_exposure(){};
+ virtual void _update_auto_exposure() {}
public:
virtual RID get_rid() const override;
diff --git a/scene/resources/camera_texture.cpp b/scene/resources/camera_texture.cpp
index b575a099ed..b219f89e59 100644
--- a/scene/resources/camera_texture.cpp
+++ b/scene/resources/camera_texture.cpp
@@ -47,6 +47,11 @@ void CameraTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "camera_is_active"), "set_camera_active", "get_camera_active");
}
+void CameraTexture::_on_format_changed() {
+ // FIXME: `emit_changed` is more appropriate, but causes errors for some reason.
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
+}
+
int CameraTexture::get_width() const {
Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
if (feed.is_valid()) {
@@ -82,13 +87,26 @@ RID CameraTexture::get_rid() const {
}
Ref<Image> CameraTexture::get_image() const {
- // not (yet) supported
- return Ref<Image>();
+ return RenderingServer::get_singleton()->texture_2d_get(get_rid());
}
void CameraTexture::set_camera_feed_id(int p_new_id) {
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ if (feed->is_connected("format_changed", callable_mp(this, &CameraTexture::_on_format_changed))) {
+ feed->disconnect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
+ }
+ }
+
camera_feed_id = p_new_id;
+
+ feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+ if (feed.is_valid()) {
+ feed->connect("format_changed", callable_mp(this, &CameraTexture::_on_format_changed));
+ }
+
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
int CameraTexture::get_camera_feed_id() const {
@@ -98,6 +116,7 @@ int CameraTexture::get_camera_feed_id() const {
void CameraTexture::set_which_feed(CameraServer::FeedImage p_which) {
which_feed = p_which;
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
CameraServer::FeedImage CameraTexture::get_which_feed() const {
@@ -109,6 +128,7 @@ void CameraTexture::set_camera_active(bool p_active) {
if (feed.is_valid()) {
feed->set_active(p_active);
notify_property_list_changed();
+ callable_mp((Resource *)this, &Resource::emit_changed).call_deferred();
}
}
diff --git a/scene/resources/camera_texture.h b/scene/resources/camera_texture.h
index 521121f9ea..dd216a72d6 100644
--- a/scene/resources/camera_texture.h
+++ b/scene/resources/camera_texture.h
@@ -43,6 +43,7 @@ private:
protected:
static void _bind_methods();
+ void _on_format_changed();
public:
virtual int get_width() const override;
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index 76e99aca92..6f43106ea9 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -274,6 +274,8 @@ void CanvasItemMaterial::_bind_methods() {
CanvasItemMaterial::CanvasItemMaterial() :
element(this) {
+ _set_material(RS::get_singleton()->material_create());
+
set_particles_anim_h_frames(1);
set_particles_anim_v_frames(1);
set_particles_anim_loop(false);
diff --git a/scene/resources/external_texture.cpp b/scene/resources/external_texture.cpp
new file mode 100644
index 0000000000..0552bbd081
--- /dev/null
+++ b/scene/resources/external_texture.cpp
@@ -0,0 +1,89 @@
+/**************************************************************************/
+/* external_texture.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "external_texture.h"
+
+void ExternalTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &ExternalTexture::set_size);
+ ClassDB::bind_method(D_METHOD("get_external_texture_id"), &ExternalTexture::get_external_texture_id);
+ ClassDB::bind_method(D_METHOD("set_external_buffer_id", "external_buffer_id"), &ExternalTexture::set_external_buffer_id);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+uint64_t ExternalTexture::get_external_texture_id() const {
+ return RenderingServer::get_singleton()->texture_get_native_handle(texture);
+}
+
+void ExternalTexture::set_size(const Size2 &p_size) {
+ if (p_size.width > 0 && p_size.height > 0 && p_size != size) {
+ size = p_size;
+ RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
+ emit_changed();
+ }
+}
+
+Size2 ExternalTexture::get_size() const {
+ return size;
+}
+
+void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) {
+ if (p_external_buffer != external_buffer) {
+ external_buffer = p_external_buffer;
+ RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
+ }
+}
+
+int ExternalTexture::get_width() const {
+ return size.width;
+}
+
+int ExternalTexture::get_height() const {
+ return size.height;
+}
+
+bool ExternalTexture::has_alpha() const {
+ return false;
+}
+
+RID ExternalTexture::get_rid() const {
+ return texture;
+}
+
+ExternalTexture::ExternalTexture() {
+ texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height);
+}
+
+ExternalTexture::~ExternalTexture() {
+ if (texture.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
diff --git a/scene/resources/external_texture.h b/scene/resources/external_texture.h
new file mode 100644
index 0000000000..96bcd8d0fe
--- /dev/null
+++ b/scene/resources/external_texture.h
@@ -0,0 +1,66 @@
+/**************************************************************************/
+/* external_texture.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef EXTERNAL_TEXTURE_H
+#define EXTERNAL_TEXTURE_H
+
+#include "scene/resources/texture.h"
+
+// External textures as defined by OES_EGL_image_external (GLES) or VK_ANDROID_external_memory_android_hardware_buffer (Vulkan).
+class ExternalTexture : public Texture2D {
+ GDCLASS(ExternalTexture, Texture2D);
+
+private:
+ RID texture;
+ Size2 size = Size2(256, 256);
+ uint64_t external_buffer = 0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ uint64_t get_external_texture_id() const;
+
+ virtual Size2 get_size() const override;
+ void set_size(const Size2 &p_size);
+
+ void set_external_buffer_id(uint64_t p_external_buffer);
+
+ virtual int get_width() const override;
+ virtual int get_height() const override;
+
+ virtual RID get_rid() const override;
+ virtual bool has_alpha() const override;
+
+ ExternalTexture();
+ ~ExternalTexture();
+};
+
+#endif // EXTERNAL_TEXTURE_H
diff --git a/scene/resources/gradient_texture.cpp b/scene/resources/gradient_texture.cpp
index 6ec9422d2d..2b0e455efb 100644
--- a/scene/resources/gradient_texture.cpp
+++ b/scene/resources/gradient_texture.cpp
@@ -85,7 +85,7 @@ void GradientTexture1D::_queue_update() {
callable_mp(this, &GradientTexture1D::update_now).call_deferred();
}
-void GradientTexture1D::_update() {
+void GradientTexture1D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@@ -172,14 +172,14 @@ RID GradientTexture1D::get_rid() const {
}
Ref<Image> GradientTexture1D::get_image() const {
- const_cast<GradientTexture1D *>(this)->update_now();
+ update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
-void GradientTexture1D::update_now() {
+void GradientTexture1D::update_now() const {
if (update_pending) {
_update();
}
@@ -225,7 +225,7 @@ void GradientTexture2D::_queue_update() {
callable_mp(this, &GradientTexture2D::update_now).call_deferred();
}
-void GradientTexture2D::_update() {
+void GradientTexture2D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@@ -405,14 +405,14 @@ RID GradientTexture2D::get_rid() const {
}
Ref<Image> GradientTexture2D::get_image() const {
- const_cast<GradientTexture2D *>(this)->update_now();
+ update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
-void GradientTexture2D::update_now() {
+void GradientTexture2D::update_now() const {
if (update_pending) {
_update();
}
diff --git a/scene/resources/gradient_texture.h b/scene/resources/gradient_texture.h
index 761e8d995b..764e5e6645 100644
--- a/scene/resources/gradient_texture.h
+++ b/scene/resources/gradient_texture.h
@@ -38,13 +38,13 @@ class GradientTexture1D : public Texture2D {
private:
Ref<Gradient> gradient;
- bool update_pending = false;
+ mutable bool update_pending = false;
mutable RID texture;
int width = 256;
bool use_hdr = false;
void _queue_update();
- void _update();
+ void _update() const;
protected:
static void _bind_methods();
@@ -64,7 +64,7 @@ public:
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
- void update_now();
+ void update_now() const;
GradientTexture1D();
virtual ~GradientTexture1D();
@@ -102,9 +102,9 @@ private:
float _get_gradient_offset_at(int x, int y) const;
- bool update_pending = false;
+ mutable bool update_pending = false;
void _queue_update();
- void _update();
+ void _update() const;
protected:
static void _bind_methods();
@@ -134,7 +134,7 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
- void update_now();
+ void update_now() const;
GradientTexture2D();
virtual ~GradientTexture2D();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 927e76e4b2..ecc1982aa5 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -46,11 +46,15 @@ void Material::set_next_pass(const Ref<Material> &p_pass) {
}
next_pass = p_pass;
- RID next_pass_rid;
- if (next_pass.is_valid()) {
- next_pass_rid = next_pass->get_rid();
+
+ if (material.is_valid()) {
+ RID next_pass_rid;
+ if (next_pass.is_valid()) {
+ next_pass_rid = next_pass->get_rid();
+ }
+
+ RS::get_singleton()->material_set_next_pass(material, next_pass_rid);
}
- RS::get_singleton()->material_set_next_pass(material, next_pass_rid);
}
Ref<Material> Material::get_next_pass() const {
@@ -61,7 +65,10 @@ void Material::set_render_priority(int p_priority) {
ERR_FAIL_COND(p_priority < RENDER_PRIORITY_MIN);
ERR_FAIL_COND(p_priority > RENDER_PRIORITY_MAX);
render_priority = p_priority;
- RS::get_singleton()->material_set_render_priority(material, p_priority);
+
+ if (material.is_valid()) {
+ RS::get_singleton()->material_set_render_priority(material, p_priority);
+ }
}
int Material::get_render_priority() const {
@@ -113,12 +120,12 @@ void Material::inspect_native_shader_code() {
RID Material::get_shader_rid() const {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret);
+ GDVIRTUAL_CALL(_get_shader_rid, ret);
return ret;
}
Shader::Mode Material::get_shader_mode() const {
Shader::Mode ret = Shader::MODE_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret);
+ GDVIRTUAL_CALL(_get_shader_mode, ret);
return ret;
}
@@ -165,13 +172,14 @@ void Material::_bind_methods() {
}
Material::Material() {
- material = RenderingServer::get_singleton()->material_create();
render_priority = 0;
}
Material::~Material() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RenderingServer::get_singleton()->free(material);
+ if (material.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(material);
+ }
}
///////////////////////////////////
@@ -374,14 +382,11 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const {
bool ShaderMaterial::_property_can_revert(const StringName &p_name) const {
if (shader.is_valid()) {
- const StringName *pr = remap_cache.getptr(p_name);
- if (pr) {
- Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), *pr);
- Variant current_value = get_shader_parameter(*pr);
- return default_value.get_type() != Variant::NIL && default_value != current_value;
- } else if (p_name == "render_priority" || p_name == "next_pass") {
+ if (remap_cache.has(p_name)) {
return true;
}
+ const String sname = p_name;
+ return sname == "render_priority" || sname == "next_pass";
}
return false;
}
@@ -422,7 +427,11 @@ void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) {
}
}
- RS::get_singleton()->material_set_shader(_get_material(), rid);
+ RID material_rid = _get_material();
+ if (material_rid.is_valid()) {
+ RS::get_singleton()->material_set_shader(material_rid, rid);
+ }
+
notify_property_list_changed(); //properties for shader exposed
emit_changed();
}
@@ -432,9 +441,12 @@ Ref<Shader> ShaderMaterial::get_shader() const {
}
void ShaderMaterial::set_shader_parameter(const StringName &p_param, const Variant &p_value) {
+ RID material_rid = _get_material();
if (p_value.get_type() == Variant::NIL) {
param_cache.erase(p_param);
- RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
+ if (material_rid.is_valid()) {
+ RS::get_singleton()->material_set_param(material_rid, p_param, Variant());
+ }
} else {
Variant *v = param_cache.getptr(p_param);
if (!v) {
@@ -449,12 +461,15 @@ void ShaderMaterial::set_shader_parameter(const StringName &p_param, const Varia
RID tex_rid = p_value;
if (tex_rid == RID()) {
param_cache.erase(p_param);
- RS::get_singleton()->material_set_param(_get_material(), p_param, Variant());
- } else {
- RS::get_singleton()->material_set_param(_get_material(), p_param, tex_rid);
+
+ if (material_rid.is_valid()) {
+ RS::get_singleton()->material_set_param(material_rid, p_param, Variant());
+ }
+ } else if (material_rid.is_valid()) {
+ RS::get_singleton()->material_set_param(material_rid, p_param, tex_rid);
}
- } else {
- RS::get_singleton()->material_set_param(_get_material(), p_param, p_value);
+ } else if (material_rid.is_valid()) {
+ RS::get_singleton()->material_set_param(material_rid, p_param, p_value);
}
}
}
@@ -471,6 +486,32 @@ void ShaderMaterial::_shader_changed() {
notify_property_list_changed(); //update all properties
}
+void ShaderMaterial::_check_material_rid() const {
+ MutexLock lock(material_rid_mutex);
+ if (_get_material().is_null()) {
+ RID shader_rid = shader.is_valid() ? shader->get_rid() : RID();
+ RID next_pass_rid;
+ if (get_next_pass().is_valid()) {
+ next_pass_rid = get_next_pass()->get_rid();
+ }
+
+ _set_material(RS::get_singleton()->material_create_from_shader(next_pass_rid, get_render_priority(), shader_rid));
+
+ for (KeyValue<StringName, Variant> param : param_cache) {
+ if (param.value.get_type() == Variant::OBJECT) {
+ RID tex_rid = param.value;
+ if (tex_rid.is_valid()) {
+ RS::get_singleton()->material_set_param(_get_material(), param.key, tex_rid);
+ } else {
+ RS::get_singleton()->material_set_param(_get_material(), param.key, Variant());
+ }
+ } else {
+ RS::get_singleton()->material_set_param(_get_material(), param.key, param.value);
+ }
+ }
+ }
+}
+
void ShaderMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader);
ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader);
@@ -511,6 +552,12 @@ Shader::Mode ShaderMaterial::get_shader_mode() const {
return Shader::MODE_SPATIAL;
}
}
+
+RID ShaderMaterial::get_rid() const {
+ _check_material_rid();
+ return Material::get_rid();
+}
+
RID ShaderMaterial::get_shader_rid() const {
if (shader.is_valid()) {
return shader->get_rid();
@@ -520,6 +567,7 @@ RID ShaderMaterial::get_shader_rid() const {
}
ShaderMaterial::ShaderMaterial() {
+ // Material RID will be empty until it is required.
}
ShaderMaterial::~ShaderMaterial() {
@@ -527,9 +575,8 @@ ShaderMaterial::~ShaderMaterial() {
/////////////////////////////////
-Mutex BaseMaterial3D::material_mutex;
-SelfList<BaseMaterial3D>::List BaseMaterial3D::dirty_materials;
HashMap<BaseMaterial3D::MaterialKey, BaseMaterial3D::ShaderData, BaseMaterial3D::MaterialKey> BaseMaterial3D::shader_map;
+Mutex BaseMaterial3D::shader_map_mutex;
BaseMaterial3D::ShaderNames *BaseMaterial3D::shader_names = nullptr;
void BaseMaterial3D::init_shaders() {
@@ -619,22 +666,31 @@ HashMap<uint64_t, Ref<StandardMaterial3D>> BaseMaterial3D::materials_for_2d;
void BaseMaterial3D::finish_shaders() {
materials_for_2d.clear();
- dirty_materials.clear();
-
memdelete(shader_names);
shader_names = nullptr;
}
+void BaseMaterial3D::_mark_dirty() {
+ dirty = true;
+}
+
void BaseMaterial3D::_update_shader() {
+ if (!dirty) {
+ return;
+ }
+
+ dirty = false;
+
MaterialKey mk = _compute_key();
if (mk == current_key) {
return; //no update required in the end
}
+ MutexLock lock(shader_map_mutex);
if (shader_map.has(current_key)) {
shader_map[current_key].users--;
if (shader_map[current_key].users == 0) {
- //deallocate shader, as it's no longer in use
+ // Deallocate shader which is no longer in use.
RS::get_singleton()->free(shader_map[current_key].shader);
shader_map.erase(current_key);
}
@@ -643,8 +699,13 @@ void BaseMaterial3D::_update_shader() {
current_key = mk;
if (shader_map.has(mk)) {
- RS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+ shader_rid = shader_map[mk].shader;
shader_map[mk].users++;
+
+ if (_get_material().is_valid()) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_rid);
+ }
+
return;
}
@@ -946,7 +1007,7 @@ uniform vec4 refraction_texture_channel;
code += "uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear_mipmap;\n";
}
- if (proximity_fade_enabled) {
+ if (features[FEATURE_REFRACTION] || proximity_fade_enabled) {
code += "uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;\n";
}
@@ -1627,7 +1688,14 @@ void fragment() {)";
}
code += R"(
float ref_amount = 1.0 - albedo.a * albedo_tex.a;
- EMISSION += textureLod(screen_texture, ref_ofs, ROUGHNESS * 8.0).rgb * ref_amount * EXPOSURE;
+
+ float refraction_depth_tex = textureLod(depth_texture, ref_ofs, 0.0).r;
+ vec4 refraction_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, refraction_depth_tex, 1.0);
+ refraction_view_pos.xyz /= refraction_view_pos.w;
+
+ // If the depth buffer is lower then the model's Z position, use the refracted UV, otherwise use the normal screen UV.
+ // At low depth differences, decrease refraction intensity to avoid sudden discontinuities.
+ EMISSION += textureLod(screen_texture, mix(SCREEN_UV, ref_ofs, smoothstep(0.0, 1.0, VERTEX.z - refraction_view_pos.z)), ROUGHNESS * 8.0).rgb * ref_amount * EXPOSURE;
ALBEDO *= 1.0 - ref_amount;
// Force transparency on the material (required for refraction).
ALPHA = 1.0;
@@ -1649,10 +1717,10 @@ void fragment() {)";
if (proximity_fade_enabled) {
code += R"(
// Proximity Fade: Enabled
- float depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
- vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth_tex, 1.0);
- world_pos.xyz /= world_pos.w;
- ALPHA *= clamp(1.0 - smoothstep(world_pos.z + proximity_fade_distance, world_pos.z, VERTEX.z), 0.0, 1.0);
+ float proximity_depth_tex = textureLod(depth_texture, SCREEN_UV, 0.0).r;
+ vec4 proximity_view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, proximity_depth_tex, 1.0);
+ proximity_view_pos.xyz /= proximity_view_pos.w;
+ ALPHA *= clamp(1.0 - smoothstep(proximity_view_pos.z + proximity_fade_distance, proximity_view_pos.z, VERTEX.z), 0.0, 1.0);
)";
}
@@ -1866,37 +1934,45 @@ void fragment() {)";
code += "}\n";
ShaderData shader_data;
- shader_data.shader = RS::get_singleton()->shader_create();
+ shader_data.shader = RS::get_singleton()->shader_create_from_code(code);
shader_data.users = 1;
-
- RS::get_singleton()->shader_set_code(shader_data.shader, code);
-
shader_map[mk] = shader_data;
+ shader_rid = shader_data.shader;
- RS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+ if (_get_material().is_valid()) {
+ RS::get_singleton()->material_set_shader(_get_material(), shader_rid);
+ }
}
-void BaseMaterial3D::flush_changes() {
- MutexLock lock(material_mutex);
+void BaseMaterial3D::_check_material_rid() {
+ MutexLock lock(material_rid_mutex);
+ if (_get_material().is_null()) {
+ RID next_pass_rid;
+ if (get_next_pass().is_valid()) {
+ next_pass_rid = get_next_pass()->get_rid();
+ }
+
+ _set_material(RS::get_singleton()->material_create_from_shader(next_pass_rid, get_render_priority(), shader_rid));
+
+ for (KeyValue<StringName, Variant> param : pending_params) {
+ RS::get_singleton()->material_set_param(_get_material(), param.key, param.value);
+ }
- while (dirty_materials.first()) {
- dirty_materials.first()->self()->_update_shader();
- dirty_materials.first()->remove_from_list();
+ pending_params.clear();
}
}
-void BaseMaterial3D::_queue_shader_change() {
- MutexLock lock(material_mutex);
-
- if (_is_initialized() && !element.in_list()) {
- dirty_materials.add(&element);
+void BaseMaterial3D::_material_set_param(const StringName &p_name, const Variant &p_value) {
+ if (_get_material().is_valid()) {
+ RS::get_singleton()->material_set_param(_get_material(), p_name, p_value);
+ } else {
+ pending_params[p_name] = p_value;
}
}
void BaseMaterial3D::set_albedo(const Color &p_albedo) {
albedo = p_albedo;
-
- RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo, p_albedo);
+ _material_set_param(shader_names->albedo, p_albedo);
}
Color BaseMaterial3D::get_albedo() const {
@@ -1905,7 +1981,7 @@ Color BaseMaterial3D::get_albedo() const {
void BaseMaterial3D::set_specular(float p_specular) {
specular = p_specular;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->specular, p_specular);
+ _material_set_param(shader_names->specular, p_specular);
}
float BaseMaterial3D::get_specular() const {
@@ -1914,7 +1990,7 @@ float BaseMaterial3D::get_specular() const {
void BaseMaterial3D::set_roughness(float p_roughness) {
roughness = p_roughness;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->roughness, p_roughness);
+ _material_set_param(shader_names->roughness, p_roughness);
}
float BaseMaterial3D::get_roughness() const {
@@ -1923,7 +1999,7 @@ float BaseMaterial3D::get_roughness() const {
void BaseMaterial3D::set_metallic(float p_metallic) {
metallic = p_metallic;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->metallic, p_metallic);
+ _material_set_param(shader_names->metallic, p_metallic);
}
float BaseMaterial3D::get_metallic() const {
@@ -1932,7 +2008,7 @@ float BaseMaterial3D::get_metallic() const {
void BaseMaterial3D::set_emission(const Color &p_emission) {
emission = p_emission;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->emission, p_emission);
+ _material_set_param(shader_names->emission, p_emission);
}
Color BaseMaterial3D::get_emission() const {
@@ -1941,10 +2017,11 @@ Color BaseMaterial3D::get_emission() const {
void BaseMaterial3D::set_emission_energy_multiplier(float p_emission_energy_multiplier) {
emission_energy_multiplier = p_emission_energy_multiplier;
+
if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) {
- RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity);
+ _material_set_param(shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity);
} else {
- RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier);
+ _material_set_param(shader_names->emission_energy, p_emission_energy_multiplier);
}
}
@@ -1955,7 +2032,7 @@ float BaseMaterial3D::get_emission_energy_multiplier() const {
void BaseMaterial3D::set_emission_intensity(float p_emission_intensity) {
ERR_FAIL_COND_EDMSG(!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"), "Cannot set material emission intensity when Physical Light Units disabled.");
emission_intensity = p_emission_intensity;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, emission_energy_multiplier * emission_intensity);
+ _material_set_param(shader_names->emission_energy, emission_energy_multiplier * emission_intensity);
}
float BaseMaterial3D::get_emission_intensity() const {
@@ -1964,7 +2041,7 @@ float BaseMaterial3D::get_emission_intensity() const {
void BaseMaterial3D::set_normal_scale(float p_normal_scale) {
normal_scale = p_normal_scale;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->normal_scale, p_normal_scale);
+ _material_set_param(shader_names->normal_scale, p_normal_scale);
}
float BaseMaterial3D::get_normal_scale() const {
@@ -1973,7 +2050,7 @@ float BaseMaterial3D::get_normal_scale() const {
void BaseMaterial3D::set_rim(float p_rim) {
rim = p_rim;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->rim, p_rim);
+ _material_set_param(shader_names->rim, p_rim);
}
float BaseMaterial3D::get_rim() const {
@@ -1982,7 +2059,7 @@ float BaseMaterial3D::get_rim() const {
void BaseMaterial3D::set_rim_tint(float p_rim_tint) {
rim_tint = p_rim_tint;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->rim_tint, p_rim_tint);
+ _material_set_param(shader_names->rim_tint, p_rim_tint);
}
float BaseMaterial3D::get_rim_tint() const {
@@ -1991,7 +2068,7 @@ float BaseMaterial3D::get_rim_tint() const {
void BaseMaterial3D::set_ao_light_affect(float p_ao_light_affect) {
ao_light_affect = p_ao_light_affect;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->ao_light_affect, p_ao_light_affect);
+ _material_set_param(shader_names->ao_light_affect, p_ao_light_affect);
}
float BaseMaterial3D::get_ao_light_affect() const {
@@ -2000,7 +2077,7 @@ float BaseMaterial3D::get_ao_light_affect() const {
void BaseMaterial3D::set_clearcoat(float p_clearcoat) {
clearcoat = p_clearcoat;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat, p_clearcoat);
+ _material_set_param(shader_names->clearcoat, p_clearcoat);
}
float BaseMaterial3D::get_clearcoat() const {
@@ -2009,7 +2086,7 @@ float BaseMaterial3D::get_clearcoat() const {
void BaseMaterial3D::set_clearcoat_roughness(float p_clearcoat_roughness) {
clearcoat_roughness = p_clearcoat_roughness;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->clearcoat_roughness, p_clearcoat_roughness);
+ _material_set_param(shader_names->clearcoat_roughness, p_clearcoat_roughness);
}
float BaseMaterial3D::get_clearcoat_roughness() const {
@@ -2018,7 +2095,7 @@ float BaseMaterial3D::get_clearcoat_roughness() const {
void BaseMaterial3D::set_anisotropy(float p_anisotropy) {
anisotropy = p_anisotropy;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->anisotropy, p_anisotropy);
+ _material_set_param(shader_names->anisotropy, p_anisotropy);
}
float BaseMaterial3D::get_anisotropy() const {
@@ -2027,7 +2104,7 @@ float BaseMaterial3D::get_anisotropy() const {
void BaseMaterial3D::set_heightmap_scale(float p_heightmap_scale) {
heightmap_scale = p_heightmap_scale;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_scale, p_heightmap_scale);
+ _material_set_param(shader_names->heightmap_scale, p_heightmap_scale);
}
float BaseMaterial3D::get_heightmap_scale() const {
@@ -2036,7 +2113,7 @@ float BaseMaterial3D::get_heightmap_scale() const {
void BaseMaterial3D::set_subsurface_scattering_strength(float p_subsurface_scattering_strength) {
subsurface_scattering_strength = p_subsurface_scattering_strength;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->subsurface_scattering_strength, subsurface_scattering_strength);
+ _material_set_param(shader_names->subsurface_scattering_strength, subsurface_scattering_strength);
}
float BaseMaterial3D::get_subsurface_scattering_strength() const {
@@ -2045,7 +2122,7 @@ float BaseMaterial3D::get_subsurface_scattering_strength() const {
void BaseMaterial3D::set_transmittance_color(const Color &p_color) {
transmittance_color = p_color;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_color, p_color);
+ _material_set_param(shader_names->transmittance_color, p_color);
}
Color BaseMaterial3D::get_transmittance_color() const {
@@ -2054,7 +2131,7 @@ Color BaseMaterial3D::get_transmittance_color() const {
void BaseMaterial3D::set_transmittance_depth(float p_depth) {
transmittance_depth = p_depth;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_depth, p_depth);
+ _material_set_param(shader_names->transmittance_depth, p_depth);
}
float BaseMaterial3D::get_transmittance_depth() const {
@@ -2063,7 +2140,7 @@ float BaseMaterial3D::get_transmittance_depth() const {
void BaseMaterial3D::set_transmittance_boost(float p_boost) {
transmittance_boost = p_boost;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->transmittance_boost, p_boost);
+ _material_set_param(shader_names->transmittance_boost, p_boost);
}
float BaseMaterial3D::get_transmittance_boost() const {
@@ -2072,7 +2149,7 @@ float BaseMaterial3D::get_transmittance_boost() const {
void BaseMaterial3D::set_backlight(const Color &p_backlight) {
backlight = p_backlight;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->backlight, backlight);
+ _material_set_param(shader_names->backlight, backlight);
}
Color BaseMaterial3D::get_backlight() const {
@@ -2081,7 +2158,7 @@ Color BaseMaterial3D::get_backlight() const {
void BaseMaterial3D::set_refraction(float p_refraction) {
refraction = p_refraction;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->refraction, refraction);
+ _material_set_param(shader_names->refraction, refraction);
}
float BaseMaterial3D::get_refraction() const {
@@ -2094,7 +2171,7 @@ void BaseMaterial3D::set_detail_uv(DetailUV p_detail_uv) {
}
detail_uv = p_detail_uv;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::DetailUV BaseMaterial3D::get_detail_uv() const {
@@ -2107,7 +2184,7 @@ void BaseMaterial3D::set_blend_mode(BlendMode p_mode) {
}
blend_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::BlendMode BaseMaterial3D::get_blend_mode() const {
@@ -2116,7 +2193,7 @@ BaseMaterial3D::BlendMode BaseMaterial3D::get_blend_mode() const {
void BaseMaterial3D::set_detail_blend_mode(BlendMode p_mode) {
detail_blend_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::BlendMode BaseMaterial3D::get_detail_blend_mode() const {
@@ -2129,7 +2206,7 @@ void BaseMaterial3D::set_transparency(Transparency p_transparency) {
}
transparency = p_transparency;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2143,7 +2220,7 @@ void BaseMaterial3D::set_alpha_antialiasing(AlphaAntiAliasing p_alpha_aa) {
}
alpha_antialiasing_mode = p_alpha_aa;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2157,7 +2234,7 @@ void BaseMaterial3D::set_shading_mode(ShadingMode p_shading_mode) {
}
shading_mode = p_shading_mode;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2171,7 +2248,7 @@ void BaseMaterial3D::set_depth_draw_mode(DepthDrawMode p_mode) {
}
depth_draw_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::DepthDrawMode BaseMaterial3D::get_depth_draw_mode() const {
@@ -2184,7 +2261,7 @@ void BaseMaterial3D::set_cull_mode(CullMode p_mode) {
}
cull_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::CullMode BaseMaterial3D::get_cull_mode() const {
@@ -2197,7 +2274,7 @@ void BaseMaterial3D::set_diffuse_mode(DiffuseMode p_mode) {
}
diffuse_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::DiffuseMode BaseMaterial3D::get_diffuse_mode() const {
@@ -2210,7 +2287,7 @@ void BaseMaterial3D::set_specular_mode(SpecularMode p_mode) {
}
specular_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::SpecularMode BaseMaterial3D::get_specular_mode() const {
@@ -2240,7 +2317,7 @@ void BaseMaterial3D::set_flag(Flags p_flag, bool p_enabled) {
update_configuration_warning();
}
- _queue_shader_change();
+ _mark_dirty();
}
bool BaseMaterial3D::get_flag(Flags p_flag) const {
@@ -2256,7 +2333,7 @@ void BaseMaterial3D::set_feature(Feature p_feature, bool p_enabled) {
features[p_feature] = p_enabled;
notify_property_list_changed();
- _queue_shader_change();
+ _mark_dirty();
}
bool BaseMaterial3D::get_feature(Feature p_feature) const {
@@ -2269,15 +2346,14 @@ void BaseMaterial3D::set_texture(TextureParam p_param, const Ref<Texture2D> &p_t
textures[p_param] = p_texture;
Variant rid = p_texture.is_valid() ? Variant(p_texture->get_rid()) : Variant();
- RS::get_singleton()->material_set_param(_get_material(), shader_names->texture_names[p_param], rid);
+ _material_set_param(shader_names->texture_names[p_param], rid);
if (p_texture.is_valid() && p_param == TEXTURE_ALBEDO) {
- RS::get_singleton()->material_set_param(_get_material(), shader_names->albedo_texture_size,
- Vector2i(p_texture->get_width(), p_texture->get_height()));
+ _material_set_param(shader_names->albedo_texture_size, Vector2i(p_texture->get_width(), p_texture->get_height()));
}
notify_property_list_changed();
- _queue_shader_change();
+ _mark_dirty();
}
Ref<Texture2D> BaseMaterial3D::get_texture(TextureParam p_param) const {
@@ -2297,7 +2373,7 @@ Ref<Texture2D> BaseMaterial3D::get_texture_by_name(const StringName &p_name) con
void BaseMaterial3D::set_texture_filter(TextureFilter p_filter) {
texture_filter = p_filter;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::TextureFilter BaseMaterial3D::get_texture_filter() const {
@@ -2469,7 +2545,7 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const {
void BaseMaterial3D::set_point_size(float p_point_size) {
point_size = p_point_size;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->point_size, p_point_size);
+ _material_set_param(shader_names->point_size, p_point_size);
}
float BaseMaterial3D::get_point_size() const {
@@ -2478,7 +2554,7 @@ float BaseMaterial3D::get_point_size() const {
void BaseMaterial3D::set_uv1_scale(const Vector3 &p_scale) {
uv1_scale = p_scale;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_scale, p_scale);
+ _material_set_param(shader_names->uv1_scale, p_scale);
}
Vector3 BaseMaterial3D::get_uv1_scale() const {
@@ -2487,7 +2563,7 @@ Vector3 BaseMaterial3D::get_uv1_scale() const {
void BaseMaterial3D::set_uv1_offset(const Vector3 &p_offset) {
uv1_offset = p_offset;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_offset, p_offset);
+ _material_set_param(shader_names->uv1_offset, p_offset);
}
Vector3 BaseMaterial3D::get_uv1_offset() const {
@@ -2497,7 +2573,7 @@ Vector3 BaseMaterial3D::get_uv1_offset() const {
void BaseMaterial3D::set_uv1_triplanar_blend_sharpness(float p_sharpness) {
// Negative values or values higher than 150 can result in NaNs, leading to broken rendering.
uv1_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0);
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv1_blend_sharpness, uv1_triplanar_sharpness);
+ _material_set_param(shader_names->uv1_blend_sharpness, uv1_triplanar_sharpness);
}
float BaseMaterial3D::get_uv1_triplanar_blend_sharpness() const {
@@ -2506,7 +2582,7 @@ float BaseMaterial3D::get_uv1_triplanar_blend_sharpness() const {
void BaseMaterial3D::set_uv2_scale(const Vector3 &p_scale) {
uv2_scale = p_scale;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_scale, p_scale);
+ _material_set_param(shader_names->uv2_scale, p_scale);
}
Vector3 BaseMaterial3D::get_uv2_scale() const {
@@ -2515,7 +2591,7 @@ Vector3 BaseMaterial3D::get_uv2_scale() const {
void BaseMaterial3D::set_uv2_offset(const Vector3 &p_offset) {
uv2_offset = p_offset;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_offset, p_offset);
+ _material_set_param(shader_names->uv2_offset, p_offset);
}
Vector3 BaseMaterial3D::get_uv2_offset() const {
@@ -2525,7 +2601,7 @@ Vector3 BaseMaterial3D::get_uv2_offset() const {
void BaseMaterial3D::set_uv2_triplanar_blend_sharpness(float p_sharpness) {
// Negative values or values higher than 150 can result in NaNs, leading to broken rendering.
uv2_triplanar_sharpness = CLAMP(p_sharpness, 0.0, 150.0);
- RS::get_singleton()->material_set_param(_get_material(), shader_names->uv2_blend_sharpness, uv2_triplanar_sharpness);
+ _material_set_param(shader_names->uv2_blend_sharpness, uv2_triplanar_sharpness);
}
float BaseMaterial3D::get_uv2_triplanar_blend_sharpness() const {
@@ -2534,7 +2610,7 @@ float BaseMaterial3D::get_uv2_triplanar_blend_sharpness() const {
void BaseMaterial3D::set_billboard_mode(BillboardMode p_mode) {
billboard_mode = p_mode;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2544,7 +2620,7 @@ BaseMaterial3D::BillboardMode BaseMaterial3D::get_billboard_mode() const {
void BaseMaterial3D::set_particles_anim_h_frames(int p_frames) {
particles_anim_h_frames = p_frames;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_h_frames, p_frames);
+ _material_set_param(shader_names->particles_anim_h_frames, p_frames);
}
int BaseMaterial3D::get_particles_anim_h_frames() const {
@@ -2553,7 +2629,7 @@ int BaseMaterial3D::get_particles_anim_h_frames() const {
void BaseMaterial3D::set_particles_anim_v_frames(int p_frames) {
particles_anim_v_frames = p_frames;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_v_frames, p_frames);
+ _material_set_param(shader_names->particles_anim_v_frames, p_frames);
}
int BaseMaterial3D::get_particles_anim_v_frames() const {
@@ -2562,7 +2638,7 @@ int BaseMaterial3D::get_particles_anim_v_frames() const {
void BaseMaterial3D::set_particles_anim_loop(bool p_loop) {
particles_anim_loop = p_loop;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->particles_anim_loop, particles_anim_loop);
+ _material_set_param(shader_names->particles_anim_loop, particles_anim_loop);
}
bool BaseMaterial3D::get_particles_anim_loop() const {
@@ -2571,7 +2647,7 @@ bool BaseMaterial3D::get_particles_anim_loop() const {
void BaseMaterial3D::set_heightmap_deep_parallax(bool p_enable) {
deep_parallax = p_enable;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2581,7 +2657,7 @@ bool BaseMaterial3D::is_heightmap_deep_parallax_enabled() const {
void BaseMaterial3D::set_heightmap_deep_parallax_min_layers(int p_layer) {
deep_parallax_min_layers = p_layer;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_min_layers, p_layer);
+ _material_set_param(shader_names->heightmap_min_layers, p_layer);
}
int BaseMaterial3D::get_heightmap_deep_parallax_min_layers() const {
@@ -2590,7 +2666,7 @@ int BaseMaterial3D::get_heightmap_deep_parallax_min_layers() const {
void BaseMaterial3D::set_heightmap_deep_parallax_max_layers(int p_layer) {
deep_parallax_max_layers = p_layer;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_max_layers, p_layer);
+ _material_set_param(shader_names->heightmap_max_layers, p_layer);
}
int BaseMaterial3D::get_heightmap_deep_parallax_max_layers() const {
@@ -2599,7 +2675,7 @@ int BaseMaterial3D::get_heightmap_deep_parallax_max_layers() const {
void BaseMaterial3D::set_heightmap_deep_parallax_flip_tangent(bool p_flip) {
heightmap_parallax_flip_tangent = p_flip;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
+ _material_set_param(shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
}
bool BaseMaterial3D::get_heightmap_deep_parallax_flip_tangent() const {
@@ -2608,7 +2684,7 @@ bool BaseMaterial3D::get_heightmap_deep_parallax_flip_tangent() const {
void BaseMaterial3D::set_heightmap_deep_parallax_flip_binormal(bool p_flip) {
heightmap_parallax_flip_binormal = p_flip;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
+ _material_set_param(shader_names->heightmap_flip, Vector2(heightmap_parallax_flip_tangent ? -1 : 1, heightmap_parallax_flip_binormal ? -1 : 1));
}
bool BaseMaterial3D::get_heightmap_deep_parallax_flip_binormal() const {
@@ -2617,7 +2693,7 @@ bool BaseMaterial3D::get_heightmap_deep_parallax_flip_binormal() const {
void BaseMaterial3D::set_grow_enabled(bool p_enable) {
grow_enabled = p_enable;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2627,7 +2703,7 @@ bool BaseMaterial3D::is_grow_enabled() const {
void BaseMaterial3D::set_alpha_scissor_threshold(float p_threshold) {
alpha_scissor_threshold = p_threshold;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_scissor_threshold, p_threshold);
+ _material_set_param(shader_names->alpha_scissor_threshold, p_threshold);
}
float BaseMaterial3D::get_alpha_scissor_threshold() const {
@@ -2636,7 +2712,7 @@ float BaseMaterial3D::get_alpha_scissor_threshold() const {
void BaseMaterial3D::set_alpha_hash_scale(float p_scale) {
alpha_hash_scale = p_scale;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_hash_scale, p_scale);
+ _material_set_param(shader_names->alpha_hash_scale, p_scale);
}
float BaseMaterial3D::get_alpha_hash_scale() const {
@@ -2645,7 +2721,7 @@ float BaseMaterial3D::get_alpha_hash_scale() const {
void BaseMaterial3D::set_alpha_antialiasing_edge(float p_edge) {
alpha_antialiasing_edge = p_edge;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->alpha_antialiasing_edge, p_edge);
+ _material_set_param(shader_names->alpha_antialiasing_edge, p_edge);
}
float BaseMaterial3D::get_alpha_antialiasing_edge() const {
@@ -2654,7 +2730,7 @@ float BaseMaterial3D::get_alpha_antialiasing_edge() const {
void BaseMaterial3D::set_grow(float p_grow) {
grow = p_grow;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->grow, p_grow);
+ _material_set_param(shader_names->grow, p_grow);
}
float BaseMaterial3D::get_grow() const {
@@ -2676,7 +2752,7 @@ static Plane _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) {
void BaseMaterial3D::set_metallic_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
metallic_texture_channel = p_channel;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->metallic_texture_channel, _get_texture_mask(p_channel));
+ _material_set_param(shader_names->metallic_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_metallic_texture_channel() const {
@@ -2686,7 +2762,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_metallic_texture_channel() co
void BaseMaterial3D::set_roughness_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
roughness_texture_channel = p_channel;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_roughness_texture_channel() const {
@@ -2696,7 +2772,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_roughness_texture_channel() c
void BaseMaterial3D::set_ao_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
ao_texture_channel = p_channel;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->ao_texture_channel, _get_texture_mask(p_channel));
+ _material_set_param(shader_names->ao_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_ao_texture_channel() const {
@@ -2706,7 +2782,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_ao_texture_channel() const {
void BaseMaterial3D::set_refraction_texture_channel(TextureChannel p_channel) {
ERR_FAIL_INDEX(p_channel, 5);
refraction_texture_channel = p_channel;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->refraction_texture_channel, _get_texture_mask(p_channel));
+ _material_set_param(shader_names->refraction_texture_channel, _get_texture_mask(p_channel));
}
BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() const {
@@ -2768,7 +2844,7 @@ void BaseMaterial3D::set_on_top_of_alpha() {
void BaseMaterial3D::set_proximity_fade_enabled(bool p_enable) {
proximity_fade_enabled = p_enable;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2778,7 +2854,7 @@ bool BaseMaterial3D::is_proximity_fade_enabled() const {
void BaseMaterial3D::set_proximity_fade_distance(float p_distance) {
proximity_fade_distance = MAX(p_distance, 0.01);
- RS::get_singleton()->material_set_param(_get_material(), shader_names->proximity_fade_distance, proximity_fade_distance);
+ _material_set_param(shader_names->proximity_fade_distance, proximity_fade_distance);
}
float BaseMaterial3D::get_proximity_fade_distance() const {
@@ -2787,7 +2863,7 @@ float BaseMaterial3D::get_proximity_fade_distance() const {
void BaseMaterial3D::set_msdf_pixel_range(float p_range) {
msdf_pixel_range = p_range;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_pixel_range, p_range);
+ _material_set_param(shader_names->msdf_pixel_range, p_range);
}
float BaseMaterial3D::get_msdf_pixel_range() const {
@@ -2796,7 +2872,7 @@ float BaseMaterial3D::get_msdf_pixel_range() const {
void BaseMaterial3D::set_msdf_outline_size(float p_size) {
msdf_outline_size = p_size;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->msdf_outline_size, p_size);
+ _material_set_param(shader_names->msdf_outline_size, p_size);
}
float BaseMaterial3D::get_msdf_outline_size() const {
@@ -2805,7 +2881,7 @@ float BaseMaterial3D::get_msdf_outline_size() const {
void BaseMaterial3D::set_distance_fade(DistanceFadeMode p_mode) {
distance_fade = p_mode;
- _queue_shader_change();
+ _mark_dirty();
notify_property_list_changed();
}
@@ -2815,7 +2891,7 @@ BaseMaterial3D::DistanceFadeMode BaseMaterial3D::get_distance_fade() const {
void BaseMaterial3D::set_distance_fade_max_distance(float p_distance) {
distance_fade_max_distance = p_distance;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->distance_fade_max, distance_fade_max_distance);
+ _material_set_param(shader_names->distance_fade_max, distance_fade_max_distance);
}
float BaseMaterial3D::get_distance_fade_max_distance() const {
@@ -2824,7 +2900,7 @@ float BaseMaterial3D::get_distance_fade_max_distance() const {
void BaseMaterial3D::set_distance_fade_min_distance(float p_distance) {
distance_fade_min_distance = p_distance;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->distance_fade_min, distance_fade_min_distance);
+ _material_set_param(shader_names->distance_fade_min, distance_fade_min_distance);
}
float BaseMaterial3D::get_distance_fade_min_distance() const {
@@ -2836,20 +2912,22 @@ void BaseMaterial3D::set_emission_operator(EmissionOperator p_op) {
return;
}
emission_op = p_op;
- _queue_shader_change();
+ _mark_dirty();
}
BaseMaterial3D::EmissionOperator BaseMaterial3D::get_emission_operator() const {
return emission_op;
}
+RID BaseMaterial3D::get_rid() const {
+ const_cast<BaseMaterial3D *>(this)->_update_shader();
+ const_cast<BaseMaterial3D *>(this)->_check_material_rid();
+ return _get_material();
+}
+
RID BaseMaterial3D::get_shader_rid() const {
- MutexLock lock(material_mutex);
- if (element.in_list()) {
- ((BaseMaterial3D *)this)->_update_shader();
- }
- ERR_FAIL_COND_V(!shader_map.has(current_key), RID());
- return shader_map[current_key].shader;
+ const_cast<BaseMaterial3D *>(this)->_update_shader();
+ return shader_rid;
}
Shader::Mode BaseMaterial3D::get_shader_mode() const {
@@ -3365,8 +3443,7 @@ void BaseMaterial3D::_bind_methods() {
BIND_ENUM_CONSTANT(DISTANCE_FADE_OBJECT_DITHER);
}
-BaseMaterial3D::BaseMaterial3D(bool p_orm) :
- element(this) {
+BaseMaterial3D::BaseMaterial3D(bool p_orm) {
orm = p_orm;
// Initialize to the same values as the shader
set_albedo(Color(1.0, 1.0, 1.0, 1.0));
@@ -3433,21 +3510,25 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) :
current_key.invalid_key = 1;
- _mark_initialized(callable_mp(this, &BaseMaterial3D::_queue_shader_change), callable_mp(this, &BaseMaterial3D::_update_shader));
+ _mark_dirty();
}
BaseMaterial3D::~BaseMaterial3D() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- MutexLock lock(material_mutex);
+ ERR_FAIL_NULL(RS::get_singleton());
- if (shader_map.has(current_key)) {
- shader_map[current_key].users--;
- if (shader_map[current_key].users == 0) {
- //deallocate shader, as it's no longer in use
- RS::get_singleton()->free(shader_map[current_key].shader);
- shader_map.erase(current_key);
+ {
+ MutexLock lock(shader_map_mutex);
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ // Deallocate shader which is no longer in use.
+ RS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
}
+ }
+ if (_get_material().is_valid()) {
RS::get_singleton()->material_set_shader(_get_material(), RID());
}
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 50a774e961..edd82b779e 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -42,7 +42,7 @@ class Material : public Resource {
RES_BASE_EXTENSION("material")
OBJ_SAVE_TYPE(Material);
- RID material;
+ mutable RID material;
Ref<Material> next_pass;
int render_priority;
@@ -55,6 +55,7 @@ class Material : public Resource {
void inspect_native_shader_code();
protected:
+ _FORCE_INLINE_ void _set_material(RID p_material) const { material = p_material; }
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
virtual bool _can_do_next_pass() const;
@@ -66,8 +67,8 @@ protected:
void _mark_initialized(const Callable &p_add_to_dirty_list, const Callable &p_update_shader);
bool _is_initialized() { return init_state == INIT_STATE_READY; }
- GDVIRTUAL0RC(RID, _get_shader_rid)
- GDVIRTUAL0RC(Shader::Mode, _get_shader_mode)
+ GDVIRTUAL0RC_REQUIRED(RID, _get_shader_rid)
+ GDVIRTUAL0RC_REQUIRED(Shader::Mode, _get_shader_mode)
GDVIRTUAL0RC(bool, _can_do_next_pass)
GDVIRTUAL0RC(bool, _can_use_render_priority)
public:
@@ -97,6 +98,7 @@ class ShaderMaterial : public Material {
mutable HashMap<StringName, StringName> remap_cache;
mutable HashMap<StringName, Variant> param_cache;
+ mutable Mutex material_rid_mutex;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -115,6 +117,7 @@ protected:
virtual bool _can_use_render_priority() const override;
void _shader_changed();
+ void _check_material_rid() const;
public:
void set_shader(const Ref<Shader> &p_shader);
@@ -125,6 +128,7 @@ public:
virtual Shader::Mode get_shader_mode() const override;
+ virtual RID get_rid() const override;
virtual RID get_shader_rid() const override;
ShaderMaterial();
@@ -136,6 +140,9 @@ class StandardMaterial3D;
class BaseMaterial3D : public Material {
GDCLASS(BaseMaterial3D, Material);
+private:
+ mutable Mutex material_rid_mutex;
+
public:
enum TextureParam {
TEXTURE_ALBEDO,
@@ -361,6 +368,7 @@ private:
};
static HashMap<MaterialKey, ShaderData, MaterialKey> shader_map;
+ static Mutex shader_map_mutex;
MaterialKey current_key;
@@ -459,16 +467,17 @@ private:
StringName albedo_texture_size;
};
- static Mutex material_mutex;
- static SelfList<BaseMaterial3D>::List dirty_materials;
static ShaderNames *shader_names;
- SelfList<BaseMaterial3D> element;
-
+ void _mark_dirty();
void _update_shader();
- _FORCE_INLINE_ void _queue_shader_change();
+ void _check_material_rid();
+ void _material_set_param(const StringName &p_name, const Variant &p_value);
bool orm;
+ bool dirty = true;
+ RID shader_rid;
+ HashMap<StringName, Variant> pending_params;
Color albedo;
float specular = 0.0f;
@@ -771,10 +780,10 @@ public:
static void init_shaders();
static void finish_shaders();
- static void flush_changes();
static Ref<Material> get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, AlphaAntiAliasing p_alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF, RID *r_shader_rid = nullptr);
+ virtual RID get_rid() const override;
virtual RID get_shader_rid() const override;
virtual Shader::Mode get_shader_mode() const override;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 22e2e9138f..4d1d733f8b 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -205,81 +205,81 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
int Mesh::get_surface_count() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret);
+ GDVIRTUAL_CALL(_get_surface_count, ret);
return ret;
}
int Mesh::surface_get_array_len(int p_idx) const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_array_len, p_idx, ret);
return ret;
}
int Mesh::surface_get_array_index_len(int p_idx) const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_array_index_len, p_idx, ret);
return ret;
}
Array Mesh::surface_get_arrays(int p_surface) const {
Array ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_arrays, p_surface, ret);
return ret;
}
TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const {
TypedArray<Array> ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
return ret;
}
Dictionary Mesh::surface_get_lods(int p_surface) const {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_lods, p_surface, ret);
return ret;
}
BitField<Mesh::ArrayFormat> Mesh::surface_get_format(int p_idx) const {
uint32_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_format, p_idx, ret);
return ret;
}
Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const {
uint32_t ret = PRIMITIVE_MAX;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_primitive_type, p_idx, ret);
return (Mesh::PrimitiveType)ret;
}
void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
- GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material);
+ GDVIRTUAL_CALL(_surface_set_material, p_idx, p_material);
}
Ref<Material> Mesh::surface_get_material(int p_idx) const {
Ref<Material> ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_material, p_idx, ret);
return ret;
}
int Mesh::get_blend_shape_count() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret);
+ GDVIRTUAL_CALL(_get_blend_shape_count, ret);
return ret;
}
StringName Mesh::get_blend_shape_name(int p_index) const {
StringName ret;
- GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret);
+ GDVIRTUAL_CALL(_get_blend_shape_name, p_index, ret);
return ret;
}
void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) {
- GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name);
+ GDVIRTUAL_CALL(_set_blend_shape_name, p_index, p_name);
}
AABB Mesh::get_aabb() const {
AABB ret;
- GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret);
+ GDVIRTUAL_CALL(_get_aabb, ret);
return ret;
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 13fd986e81..068bfb6708 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -68,20 +68,20 @@ public:
protected:
static void _bind_methods();
- GDVIRTUAL0RC(int, _get_surface_count)
- GDVIRTUAL1RC(int, _surface_get_array_len, int)
- GDVIRTUAL1RC(int, _surface_get_array_index_len, int)
- GDVIRTUAL1RC(Array, _surface_get_arrays, int)
- GDVIRTUAL1RC(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
- GDVIRTUAL1RC(Dictionary, _surface_get_lods, int)
- GDVIRTUAL1RC(uint32_t, _surface_get_format, int)
- GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int)
- GDVIRTUAL2(_surface_set_material, int, Ref<Material>)
- GDVIRTUAL1RC(Ref<Material>, _surface_get_material, int)
- GDVIRTUAL0RC(int, _get_blend_shape_count)
- GDVIRTUAL1RC(StringName, _get_blend_shape_name, int)
- GDVIRTUAL2(_set_blend_shape_name, int, StringName)
- GDVIRTUAL0RC(AABB, _get_aabb)
+ GDVIRTUAL0RC_REQUIRED(int, _get_surface_count)
+ GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_len, int)
+ GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_index_len, int)
+ GDVIRTUAL1RC_REQUIRED(Array, _surface_get_arrays, int)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _surface_get_lods, int)
+ GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_format, int)
+ GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_primitive_type, int)
+ GDVIRTUAL2_REQUIRED(_surface_set_material, int, Ref<Material>)
+ GDVIRTUAL1RC_REQUIRED(Ref<Material>, _surface_get_material, int)
+ GDVIRTUAL0RC_REQUIRED(int, _get_blend_shape_count)
+ GDVIRTUAL1RC_REQUIRED(StringName, _get_blend_shape_name, int)
+ GDVIRTUAL2_REQUIRED(_set_blend_shape_name, int, StringName)
+ GDVIRTUAL0RC_REQUIRED(AABB, _get_aabb)
public:
enum {
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 8cfe4c92b7..09bc1fa8e4 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -2261,6 +2261,8 @@ void ParticleProcessMaterial::_bind_methods() {
ParticleProcessMaterial::ParticleProcessMaterial() :
element(this) {
+ _set_material(RS::get_singleton()->material_create());
+
set_direction(Vector3(1, 0, 0));
set_spread(45);
set_flatness(0);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index d531eea311..e234a81c88 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -612,29 +612,27 @@ Error ResourceLoaderText::load() {
}
}
- if (ClassDB::has_property(res->get_class_name(), assign)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = res->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = res->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
@@ -754,29 +752,27 @@ Error ResourceLoaderText::load() {
}
}
- if (ClassDB::has_property(resource->get_class_name(), assign)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = resource->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = resource->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = resource->get(assign, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = resource->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
@@ -1708,6 +1704,8 @@ static String _resource_get_class(Ref<Resource> p_resource) {
}
Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ Resource::seed_scene_unique_id(p_path.hash()); // Seeding for save path should make it deterministic for importers.
+
if (p_path.ends_with(".tscn")) {
packed_scene = p_resource;
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 46d38146a6..24d17108d5 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -50,6 +50,14 @@ Shader::Mode Shader::get_mode() const {
return mode;
}
+void Shader::_check_shader_rid() const {
+ MutexLock lock(shader_rid_mutex);
+ if (shader_rid.is_null() && !preprocessed_code.is_empty()) {
+ shader_rid = RenderingServer::get_singleton()->shader_create_from_code(preprocessed_code, get_path());
+ preprocessed_code = String();
+ }
+}
+
void Shader::_dependency_changed() {
// Preprocess and compile the code again because a dependency has changed. It also calls emit_changed() for us.
_recompile();
@@ -61,7 +69,10 @@ void Shader::_recompile() {
void Shader::set_path(const String &p_path, bool p_take_over) {
Resource::set_path(p_path, p_take_over);
- RS::get_singleton()->shader_set_path_hint(shader, p_path);
+
+ if (shader_rid.is_valid()) {
+ RS::get_singleton()->shader_set_path_hint(shader_rid, p_path);
+ }
}
void Shader::set_include_path(const String &p_path) {
@@ -76,7 +87,7 @@ void Shader::set_code(const String &p_code) {
}
code = p_code;
- String pp_code = p_code;
+ preprocessed_code = p_code;
{
String path = get_path();
@@ -88,7 +99,7 @@ void Shader::set_code(const String &p_code) {
// 2) Server does not do interaction with Resource filetypes, this is a scene level feature.
HashSet<Ref<ShaderInclude>> new_include_dependencies;
ShaderPreprocessor preprocessor;
- Error result = preprocessor.preprocess(p_code, path, pp_code, nullptr, nullptr, nullptr, &new_include_dependencies);
+ Error result = preprocessor.preprocess(p_code, path, preprocessed_code, nullptr, nullptr, nullptr, &new_include_dependencies);
if (result == OK) {
// This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower)
include_dependencies = new_include_dependencies;
@@ -96,7 +107,7 @@ void Shader::set_code(const String &p_code) {
}
// Try to get the shader type from the final, fully preprocessed shader code.
- String type = ShaderLanguage::get_shader_type(pp_code);
+ String type = ShaderLanguage::get_shader_type(preprocessed_code);
if (type == "canvas_item") {
mode = MODE_CANVAS_ITEM;
@@ -114,7 +125,10 @@ void Shader::set_code(const String &p_code) {
E->connect_changed(callable_mp(this, &Shader::_dependency_changed));
}
- RenderingServer::get_singleton()->shader_set_code(shader, pp_code);
+ if (shader_rid.is_valid()) {
+ RenderingServer::get_singleton()->shader_set_code(shader_rid, preprocessed_code);
+ preprocessed_code = String();
+ }
emit_changed();
}
@@ -126,9 +140,10 @@ String Shader::get_code() const {
void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_groups) const {
_update_shader();
+ _check_shader_rid();
List<PropertyInfo> local;
- RenderingServer::get_singleton()->get_shader_parameter_list(shader, &local);
+ RenderingServer::get_singleton()->get_shader_parameter_list(shader_rid, &local);
#ifdef TOOLS_ENABLED
DocData::ClassDoc class_doc;
@@ -182,17 +197,20 @@ void Shader::get_shader_uniform_list(List<PropertyInfo> *p_params, bool p_get_gr
RID Shader::get_rid() const {
_update_shader();
+ _check_shader_rid();
- return shader;
+ return shader_rid;
}
void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<Texture> &p_texture, int p_index) {
+ _check_shader_rid();
+
if (p_texture.is_valid()) {
if (!default_textures.has(p_name)) {
default_textures[p_name] = HashMap<int, Ref<Texture>>();
}
default_textures[p_name][p_index] = p_texture;
- RS::get_singleton()->shader_set_default_texture_parameter(shader, p_name, p_texture->get_rid(), p_index);
+ RS::get_singleton()->shader_set_default_texture_parameter(shader_rid, p_name, p_texture->get_rid(), p_index);
} else {
if (default_textures.has(p_name) && default_textures[p_name].has(p_index)) {
default_textures[p_name].erase(p_index);
@@ -201,7 +219,7 @@ void Shader::set_default_texture_parameter(const StringName &p_name, const Ref<T
default_textures.erase(p_name);
}
}
- RS::get_singleton()->shader_set_default_texture_parameter(shader, p_name, RID(), p_index);
+ RS::get_singleton()->shader_set_default_texture_parameter(shader_rid, p_name, RID(), p_index);
}
emit_changed();
@@ -225,6 +243,7 @@ bool Shader::is_text_shader() const {
}
void Shader::_update_shader() const {
+ // Base implementation does nothing.
}
Array Shader::_get_shader_uniform_list(bool p_get_groups) {
@@ -258,12 +277,14 @@ void Shader::_bind_methods() {
}
Shader::Shader() {
- shader = RenderingServer::get_singleton()->shader_create();
+ // Shader RID will be empty until it is required.
}
Shader::~Shader() {
- ERR_FAIL_NULL(RenderingServer::get_singleton());
- RenderingServer::get_singleton()->free(shader);
+ if (shader_rid.is_valid()) {
+ ERR_FAIL_NULL(RenderingServer::get_singleton());
+ RenderingServer::get_singleton()->free(shader_rid);
+ }
}
////////////
diff --git a/scene/resources/shader.h b/scene/resources/shader.h
index 682fbd7ea6..18197419f3 100644
--- a/scene/resources/shader.h
+++ b/scene/resources/shader.h
@@ -52,7 +52,10 @@ public:
};
private:
- RID shader;
+ mutable RID shader_rid;
+ mutable String preprocessed_code;
+ mutable Mutex shader_rid_mutex;
+
Mode mode = MODE_SPATIAL;
HashSet<Ref<ShaderInclude>> include_dependencies;
String code;
@@ -60,6 +63,7 @@ private:
HashMap<StringName, HashMap<int, Ref<Texture>>> default_textures;
+ void _check_shader_rid() const;
void _dependency_changed();
void _recompile();
virtual void _update_shader() const; //used for visual shader
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index f87bf1ee05..1203c21a1b 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -90,7 +90,7 @@ Point2 StyleBox::get_offset() const {
}
void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect);
+ GDVIRTUAL_CALL(_draw, p_canvas_item, p_rect);
}
Rect2 StyleBox::get_draw_rect(const Rect2 &p_rect) const {
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 3d3a059d0b..e09b271c47 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -48,7 +48,7 @@ protected:
static void _bind_methods();
virtual float get_style_margin(Side p_side) const { return 0; }
- GDVIRTUAL2C(_draw, RID, Rect2)
+ GDVIRTUAL2C_REQUIRED(_draw, RID, Rect2)
GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2)
GDVIRTUAL0RC(Size2, _get_minimum_size)
GDVIRTUAL2RC(bool, _test_mask, Point2, Rect2)
diff --git a/scene/resources/style_box_flat.cpp b/scene/resources/style_box_flat.cpp
index 60b91ef0cb..15816925c1 100644
--- a/scene/resources/style_box_flat.cpp
+++ b/scene/resources/style_box_flat.cpp
@@ -226,33 +226,16 @@ inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_re
real_t border_right = style_rect.size.width - inner_rect.size.width - border_left;
real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top;
- real_t rad;
-
- // Top left.
- rad = MIN(border_top, border_left);
- inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0);
-
- // Top right;
- rad = MIN(border_top, border_right);
- inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0);
-
- // Bottom right.
- rad = MIN(border_bottom, border_right);
- inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0);
-
- // Bottom left.
- rad = MIN(border_bottom, border_left);
- inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0);
+ inner_corner_radius[0] = MAX(corner_radius[0] - MIN(border_top, border_left), 0); // Top left.
+ inner_corner_radius[1] = MAX(corner_radius[1] - MIN(border_top, border_right), 0); // Top right.
+ inner_corner_radius[2] = MAX(corner_radius[2] - MIN(border_bottom, border_right), 0); // Bottom right.
+ inner_corner_radius[3] = MAX(corner_radius[3] - MIN(border_bottom, border_left), 0); // Bottom left.
}
inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4],
const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) {
int vert_offset = verts.size();
- if (!vert_offset) {
- vert_offset = 0;
- }
-
- int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail;
+ int adapted_corner_detail = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0) ? corner_detail : 1;
bool draw_border = !is_filled;
@@ -280,30 +263,44 @@ inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices,
// If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this
// method either draw a ring or a filled rounded rectangle, but not both.
- int max_inner_outer = draw_border ? 2 : 1;
-
- for (int corner_index = 0; corner_index < 4; corner_index++) {
+ real_t quarter_arc_rad = Math_PI / 2.0;
+ Point2 style_rect_center = style_rect.get_center();
+
+ int colors_size = colors.size();
+ int verts_size = verts.size();
+ int new_verts_amount = (adapted_corner_detail + 1) * (draw_border ? 8 : 4);
+ colors.resize(colors_size + new_verts_amount);
+ verts.resize(verts_size + new_verts_amount);
+ Color *colors_ptr = colors.ptrw();
+ Vector2 *verts_ptr = verts.ptrw();
+
+ for (int corner_idx = 0; corner_idx < 4; corner_idx++) {
for (int detail = 0; detail <= adapted_corner_detail; detail++) {
- for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) {
- real_t radius;
- Color color;
- Point2 corner_point;
- if (inner_outer == 0) {
- radius = inner_corner_radius[corner_index];
- color = inner_color;
- corner_point = inner_points[corner_index];
- } else {
- radius = ring_corner_radius[corner_index];
- color = outer_color;
- corner_point = outer_points[corner_index];
- }
+ int idx_ofs = (adapted_corner_detail + 1) * corner_idx + detail;
+ if (draw_border) {
+ idx_ofs *= 2;
+ }
- const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x;
- const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y;
- const float x_skew = -skew.x * (y - style_rect.get_center().y);
- const float y_skew = -skew.y * (x - style_rect.get_center().x);
- verts.push_back(Vector2(x + x_skew, y + y_skew));
- colors.push_back(color);
+ const real_t pt_angle = (corner_idx + detail / (double)adapted_corner_detail) * quarter_arc_rad + Math_PI;
+ const real_t angle_cosine = cos(pt_angle);
+ const real_t angle_sine = sin(pt_angle);
+
+ {
+ const real_t x = inner_corner_radius[corner_idx] * angle_cosine + inner_points[corner_idx].x;
+ const real_t y = inner_corner_radius[corner_idx] * angle_sine + inner_points[corner_idx].y;
+ const float x_skew = -skew.x * (y - style_rect_center.y);
+ const float y_skew = -skew.y * (x - style_rect_center.x);
+ verts_ptr[verts_size + idx_ofs] = Vector2(x + x_skew, y + y_skew);
+ colors_ptr[colors_size + idx_ofs] = inner_color;
+ }
+
+ if (draw_border) {
+ const real_t x = ring_corner_radius[corner_idx] * angle_cosine + outer_points[corner_idx].x;
+ const real_t y = ring_corner_radius[corner_idx] * angle_sine + outer_points[corner_idx].y;
+ const float x_skew = -skew.x * (y - style_rect_center.y);
+ const float y_skew = -skew.y * (x - style_rect_center.x);
+ verts_ptr[verts_size + idx_ofs + 1] = Vector2(x + x_skew, y + y_skew);
+ colors_ptr[colors_size + idx_ofs + 1] = outer_color;
}
}
}
@@ -313,10 +310,15 @@ inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices,
// Fill the indices and the colors for the border.
if (draw_border) {
+ int indices_size = indices.size();
+ indices.resize(indices_size + ring_vert_count * 3);
+ int *indices_ptr = indices.ptrw();
+
for (int i = 0; i < ring_vert_count; i++) {
- indices.push_back(vert_offset + ((i + 0) % ring_vert_count));
- indices.push_back(vert_offset + ((i + 2) % ring_vert_count));
- indices.push_back(vert_offset + ((i + 1) % ring_vert_count));
+ int idx_ofs = indices_size + i * 3;
+ indices_ptr[idx_ofs] = vert_offset + i % ring_vert_count;
+ indices_ptr[idx_ofs + 1] = vert_offset + (i + 2) % ring_vert_count;
+ indices_ptr[idx_ofs + 2] = vert_offset + (i + 1) % ring_vert_count;
}
}
@@ -327,40 +329,30 @@ inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices,
int stripes_count = ring_vert_count / 2 - 1;
int last_vert_id = ring_vert_count - 1;
+ int indices_size = indices.size();
+ indices.resize(indices_size + stripes_count * 6);
+ int *indices_ptr = indices.ptrw();
+
for (int i = 0; i < stripes_count; i++) {
+ int idx_ofs = indices_size + i * 6;
// Polygon 1.
- indices.push_back(vert_offset + i);
- indices.push_back(vert_offset + last_vert_id - i - 1);
- indices.push_back(vert_offset + i + 1);
+ indices_ptr[idx_ofs] = vert_offset + i;
+ indices_ptr[idx_ofs + 1] = vert_offset + last_vert_id - i - 1;
+ indices_ptr[idx_ofs + 2] = vert_offset + i + 1;
// Polygon 2.
- indices.push_back(vert_offset + i);
- indices.push_back(vert_offset + last_vert_id - 0 - i);
- indices.push_back(vert_offset + last_vert_id - 1 - i);
+ indices_ptr[idx_ofs + 3] = vert_offset + i;
+ indices_ptr[idx_ofs + 4] = vert_offset + last_vert_id - i;
+ indices_ptr[idx_ofs + 5] = vert_offset + last_vert_id - i - 1;
}
}
}
inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) {
- if (p_values[p_index_a] + p_values[p_index_b] > p_width) {
- real_t factor;
- real_t new_value;
-
- factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]);
-
- new_value = (p_values[p_index_a] * factor);
- if (new_value < adapted_values[p_index_a]) {
- adapted_values[p_index_a] = new_value;
- }
- new_value = (p_values[p_index_b] * factor);
- if (new_value < adapted_values[p_index_b]) {
- adapted_values[p_index_b] = new_value;
- }
- } else {
- adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]);
- adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]);
- }
- adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]);
- adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]);
+ real_t value_a = p_values[p_index_a];
+ real_t value_b = p_values[p_index_b];
+ real_t factor = MIN(1.0, p_width / (value_a + value_b));
+ adapted_values[p_index_a] = MIN(MIN(value_a * factor, p_max_a), adapted_values[p_index_a]);
+ adapted_values[p_index_b] = MIN(MIN(value_b * factor, p_max_b), adapted_values[p_index_b]);
}
Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const {
@@ -388,7 +380,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
}
const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
- // Only enable antialiasing if it is actually needed. This improve performances
+ // Only enable antialiasing if it is actually needed. This improves performance
// and maximizes sharpness for non-skewed StyleBoxes with sharp corners.
const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased;
@@ -428,7 +420,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
Vector<Color> colors;
Vector<Point2> uvs;
- // Create shadow
+ // Create shadow.
if (draw_shadow) {
Rect2 shadow_inner_rect = style_rect;
shadow_inner_rect.position += shadow_offset;
@@ -538,9 +530,10 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
// Compute UV coordinates.
Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0);
uvs.resize(verts.size());
+ Point2 *uvs_ptr = uvs.ptrw();
for (int i = 0; i < verts.size(); i++) {
- uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
- uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
+ uvs_ptr[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width;
+ uvs_ptr[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height;
}
// Draw stylebox.
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 6da4215031..da90ba1ef2 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -442,7 +442,6 @@ Color CodeHighlighter::get_keyword_color(const String &p_keyword) const {
}
void CodeHighlighter::set_keyword_colors(const Dictionary p_keywords) {
- keywords.clear();
keywords = p_keywords;
clear_highlighting_cache();
}
@@ -476,7 +475,6 @@ Color CodeHighlighter::get_member_keyword_color(const String &p_member_keyword)
}
void CodeHighlighter::set_member_keyword_colors(const Dictionary &p_member_keywords) {
- member_keywords.clear();
member_keywords = p_member_keywords;
clear_highlighting_cache();
}
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 0efaad61fe..7713181a4b 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -34,13 +34,13 @@
int Texture2D::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int Texture2D::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
@@ -133,37 +133,37 @@ TypedArray<Image> Texture3D::_get_datai() const {
Image::Format Texture3D::get_format() const {
Image::Format ret = Image::FORMAT_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ GDVIRTUAL_CALL(_get_format, ret);
return ret;
}
int Texture3D::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int Texture3D::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
int Texture3D::get_depth() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_depth, ret);
+ GDVIRTUAL_CALL(_get_depth, ret);
return ret;
}
bool Texture3D::has_mipmaps() const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ GDVIRTUAL_CALL(_has_mipmaps, ret);
return ret;
}
Vector<Ref<Image>> Texture3D::get_data() const {
TypedArray<Image> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_data, ret);
+ GDVIRTUAL_CALL(_get_data, ret);
Vector<Ref<Image>> data;
data.resize(ret.size());
for (int i = 0; i < data.size(); i++) {
@@ -198,43 +198,43 @@ Ref<Resource> Texture3D::create_placeholder() const {
Image::Format TextureLayered::get_format() const {
Image::Format ret = Image::FORMAT_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ GDVIRTUAL_CALL(_get_format, ret);
return ret;
}
TextureLayered::LayeredType TextureLayered::get_layered_type() const {
uint32_t ret = LAYERED_TYPE_2D_ARRAY;
- GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret);
+ GDVIRTUAL_CALL(_get_layered_type, ret);
return (LayeredType)ret;
}
int TextureLayered::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int TextureLayered::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
int TextureLayered::get_layers() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_layers, ret);
+ GDVIRTUAL_CALL(_get_layers, ret);
return ret;
}
bool TextureLayered::has_mipmaps() const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ GDVIRTUAL_CALL(_has_mipmaps, ret);
return ret;
}
Ref<Image> TextureLayered::get_layer_data(int p_layer) const {
Ref<Image> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret);
+ GDVIRTUAL_CALL(_get_layer_data, p_layer, ret);
return ret;
}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index e7840804bf..cdad884e71 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -57,8 +57,8 @@ class Texture2D : public Texture {
protected:
static void _bind_methods();
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
GDVIRTUAL2RC(bool, _is_pixel_opaque, int, int)
GDVIRTUAL0RC(bool, _has_alpha)
@@ -93,13 +93,13 @@ class TextureLayered : public Texture {
protected:
static void _bind_methods();
- GDVIRTUAL0RC(Image::Format, _get_format)
- GDVIRTUAL0RC(uint32_t, _get_layered_type)
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
- GDVIRTUAL0RC(int, _get_layers)
- GDVIRTUAL0RC(bool, _has_mipmaps)
- GDVIRTUAL1RC(Ref<Image>, _get_layer_data, int)
+ GDVIRTUAL0RC_REQUIRED(Image::Format, _get_format)
+ GDVIRTUAL0RC_REQUIRED(uint32_t, _get_layered_type)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_layers)
+ GDVIRTUAL0RC_REQUIRED(bool, _has_mipmaps)
+ GDVIRTUAL1RC_REQUIRED(Ref<Image>, _get_layer_data, int)
public:
enum LayeredType {
LAYERED_TYPE_2D_ARRAY,
@@ -128,12 +128,12 @@ protected:
TypedArray<Image> _get_datai() const;
- GDVIRTUAL0RC(Image::Format, _get_format)
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
- GDVIRTUAL0RC(int, _get_depth)
- GDVIRTUAL0RC(bool, _has_mipmaps)
- GDVIRTUAL0RC(TypedArray<Image>, _get_data)
+ GDVIRTUAL0RC_REQUIRED(Image::Format, _get_format)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_depth)
+ GDVIRTUAL0RC_REQUIRED(bool, _has_mipmaps)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Image>, _get_data)
public:
virtual Image::Format get_format() const;
virtual int get_width() const;
diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp
index 3d31fe0491..1fcc1821dc 100644
--- a/scene/resources/video_stream.cpp
+++ b/scene/resources/video_stream.cpp
@@ -119,7 +119,7 @@ Ref<Texture2D> VideoStreamPlayback::get_texture() const {
}
void VideoStreamPlayback::update(double p_delta) {
- GDVIRTUAL_REQUIRED_CALL(_update, p_delta);
+ GDVIRTUAL_CALL(_update, p_delta);
}
void VideoStreamPlayback::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index dc54f4b769..3843cc99c4 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -56,7 +56,7 @@ protected:
GDVIRTUAL1(_seek, double);
GDVIRTUAL1(_set_audio_track, int);
GDVIRTUAL0RC(Ref<Texture2D>, _get_texture);
- GDVIRTUAL1(_update, double);
+ GDVIRTUAL1_REQUIRED(_update, double);
GDVIRTUAL0RC(int, _get_channels);
GDVIRTUAL0RC(int, _get_mix_rate);
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 26666538af..3db1ab9338 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -8081,17 +8081,28 @@ int VisualShaderNodeRemap::get_input_port_count() const {
}
VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_input_port_type(int p_port) const {
- switch (p_port) {
- case 0:
- return PORT_TYPE_SCALAR;
- case 1:
- return PORT_TYPE_SCALAR;
- case 2:
- return PORT_TYPE_SCALAR;
- case 3:
- return PORT_TYPE_SCALAR;
- case 4:
- return PORT_TYPE_SCALAR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ break;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR_3D;
+ }
+ break;
+ case OP_TYPE_VECTOR_4D:
+ return PORT_TYPE_VECTOR_4D;
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ if (p_port == 0) {
+ return PORT_TYPE_VECTOR_4D;
+ }
+ break;
default:
break;
}
@@ -8123,23 +8134,159 @@ int VisualShaderNodeRemap::get_output_port_count() const {
}
VisualShaderNodeRemap::PortType VisualShaderNodeRemap::get_output_port_type(int p_port) const {
- return PORT_TYPE_SCALAR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ case OP_TYPE_VECTOR_2D_SCALAR:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ case OP_TYPE_VECTOR_3D_SCALAR:
+ return PORT_TYPE_VECTOR_3D;
+ case OP_TYPE_VECTOR_4D:
+ case OP_TYPE_VECTOR_4D_SCALAR:
+ return PORT_TYPE_VECTOR_4D;
+ default:
+ return PORT_TYPE_SCALAR;
+ }
}
String VisualShaderNodeRemap::get_output_port_name(int p_port) const {
return "value";
}
+void VisualShaderNodeRemap::set_op_type(OpType p_op_type) {
+ ERR_FAIL_INDEX(int(p_op_type), int(OP_TYPE_MAX));
+ if (op_type == p_op_type) {
+ return;
+ }
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 1.0, get_input_port_default_value(2));
+ set_input_port_default_value(3, 0.0, get_input_port_default_value(3));
+ set_input_port_default_value(4, 1.0, get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector2(1.0, 1.0), get_input_port_default_value(2));
+ set_input_port_default_value(3, Vector2(), get_input_port_default_value(3));
+ set_input_port_default_value(4, Vector2(1.0, 1.0), get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 1.0, get_input_port_default_value(2));
+ set_input_port_default_value(3, 0.0, get_input_port_default_value(3));
+ set_input_port_default_value(4, 1.0, get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(2));
+ set_input_port_default_value(3, Vector3(), get_input_port_default_value(3));
+ set_input_port_default_value(4, Vector3(1.0, 1.0, 1.0), get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 1.0, get_input_port_default_value(2));
+ set_input_port_default_value(3, 0.0, get_input_port_default_value(3));
+ set_input_port_default_value(4, 1.0, get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Quaternion(), get_input_port_default_value(1));
+ set_input_port_default_value(2, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(2));
+ set_input_port_default_value(3, Quaternion(), get_input_port_default_value(3));
+ set_input_port_default_value(4, Quaternion(1.0, 1.0, 1.0, 1.0), get_input_port_default_value(4));
+ } break;
+ case OP_TYPE_VECTOR_4D_SCALAR: {
+ set_input_port_default_value(0, Quaternion(), get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ set_input_port_default_value(2, 1.0, get_input_port_default_value(2));
+ set_input_port_default_value(3, 0.0, get_input_port_default_value(3));
+ set_input_port_default_value(4, 1.0, get_input_port_default_value(4));
+ } break;
+ default:
+ break;
+ }
+ op_type = p_op_type;
+ emit_changed();
+}
+
+VisualShaderNodeRemap::OpType VisualShaderNodeRemap::get_op_type() const {
+ return op_type;
+}
+
String VisualShaderNodeRemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
code += " {\n";
- code += vformat(" float __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]);
- code += vformat(" float __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]);
- code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ switch (op_type) {
+ case OP_TYPE_SCALAR: {
+ code += vformat(" float __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" float __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ code += vformat(" vec2 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec2 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_2D_SCALAR: {
+ code += vformat(" vec2 __input_range = vec2(%s - %s);\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec2 __output_range = vec2(%s - %s);\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = vec2(%s) + __output_range * ((%s - vec2(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += vformat(" vec3 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec3 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_3D_SCALAR: {
+ code += vformat(" vec3 __input_range = vec3(%s - %s);\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec3 __output_range = vec3(%s - %s);\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = vec3(%s) + __output_range * ((%s - vec3(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_4D: {
+ code += vformat(" vec4 __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec4 __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_4D_SCALAR: {
+ code += vformat(" vec4 __input_range = vec4(%s - %s);\n", p_input_vars[2], p_input_vars[1]);
+ code += vformat(" vec4 __output_range = vec4(%s - %s);\n", p_input_vars[4], p_input_vars[3]);
+ code += vformat(" %s = vec4(%s) + __output_range * ((%s - vec4(%s)) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]);
+ } break;
+ default:
+ break;
+ }
code += " }\n";
return code;
}
+Vector<StringName> VisualShaderNodeRemap::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("op_type");
+ return props;
+}
+
+void VisualShaderNodeRemap::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_op_type", "op_type"), &VisualShaderNodeRemap::set_op_type);
+ ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeRemap::get_op_type);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector2Scalar,Vector3,Vector3Scalar,Vector4,Vector4Scalar"), "set_op_type", "get_op_type");
+
+ BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_4D_SCALAR);
+ BIND_ENUM_CONSTANT(OP_TYPE_MAX);
+}
+
VisualShaderNodeRemap::VisualShaderNodeRemap() {
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 1.0);
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index ff02e55fb2..67dc8f7353 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -3068,10 +3068,30 @@ public:
VisualShaderNodeRandomRange();
};
+///////////////////////////////////////
+/// Remap
+///////////////////////////////////////
+
class VisualShaderNodeRemap : public VisualShaderNode {
GDCLASS(VisualShaderNodeRemap, VisualShaderNode);
public:
+ enum OpType {
+ OP_TYPE_SCALAR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_2D_SCALAR,
+ OP_TYPE_VECTOR_3D,
+ OP_TYPE_VECTOR_3D_SCALAR,
+ OP_TYPE_VECTOR_4D,
+ OP_TYPE_VECTOR_4D_SCALAR,
+ OP_TYPE_MAX,
+ };
+
+protected:
+ OpType op_type = OP_TYPE_SCALAR;
+ static void _bind_methods();
+
+public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
@@ -3082,13 +3102,26 @@ public:
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ void set_op_type(OpType p_op_type);
+ OpType get_op_type() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- virtual Category get_category() const override { return CATEGORY_UTILITY; }
+ virtual Category get_category() const override {
+ if (op_type == OP_TYPE_SCALAR) {
+ return CATEGORY_SCALAR;
+ } else {
+ return CATEGORY_VECTOR;
+ }
+ }
VisualShaderNodeRemap();
};
+VARIANT_ENUM_CAST(VisualShaderNodeRemap::OpType)
+
class VisualShaderNodeRotationByAxis : public VisualShaderNode {
GDCLASS(VisualShaderNodeRotationByAxis, VisualShaderNode);
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index f8a0336b37..140e588291 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -141,5 +141,6 @@ SceneStringNames::SceneStringNames() {
confirmed = StaticCString::create("confirmed");
text_changed = StaticCString::create("text_changed");
+ text_submitted = StaticCString::create("text_submitted");
value_changed = StaticCString::create("value_changed");
}
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 381a161ad5..fc22be33b2 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -154,6 +154,7 @@ public:
StringName confirmed;
StringName text_changed;
+ StringName text_submitted;
StringName value_changed;
};
diff --git a/scene/theme/SCsub b/scene/theme/SCsub
index 2372d1820a..fb0914c0ee 100644
--- a/scene/theme/SCsub
+++ b/scene/theme/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index 749d4e3530..caf44ac392 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -689,6 +689,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("forward_folder", "FileDialog", icons["arrow_right"]);
theme->set_icon("reload", "FileDialog", icons["reload"]);
theme->set_icon("toggle_hidden", "FileDialog", icons["visibility_visible"]);
+ theme->set_icon("toggle_filename_filter", "FileDialog", icons["toggle_filename_filter"]);
theme->set_icon("folder", "FileDialog", icons["folder"]);
theme->set_icon("file", "FileDialog", icons["file"]);
theme->set_icon("create_folder", "FileDialog", icons["folder_create"]);
@@ -1211,6 +1212,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("v_separation", "VFlowContainer", Math::round(4 * scale));
theme->set_stylebox(SceneStringName(panel), "PanelContainer", make_flat_stylebox(style_normal_color, 0, 0, 0, 0));
+ theme->set_stylebox("split_bar_background", "SplitContainer", make_empty_stylebox(0, 0, 0, 0));
+ theme->set_stylebox("split_bar_background", "VSplitContainer", make_empty_stylebox(0, 0, 0, 0));
+ theme->set_stylebox("split_bar_background", "HSplitContainer", make_empty_stylebox(0, 0, 0, 0));
theme->set_icon("zoom_out", "GraphEdit", icons["zoom_less"]);
theme->set_icon("zoom_in", "GraphEdit", icons["zoom_more"]);
diff --git a/scene/theme/icons/SCsub b/scene/theme/icons/SCsub
index 1f3b7f6d17..19aca74e57 100644
--- a/scene/theme/icons/SCsub
+++ b/scene/theme/icons/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from misc.utility.scons_hints import *
Import("env")
diff --git a/scene/theme/icons/toggle_filename_filter.svg b/scene/theme/icons/toggle_filename_filter.svg
new file mode 100644
index 0000000000..351cc44ced
--- /dev/null
+++ b/scene/theme/icons/toggle_filename_filter.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#e0e0e0"><path d="m13.297.714h-13.013a.454.454 0 0 0 -.318.779l4.615 4.507v7.086a.45.45 0 0 0 .738.354l3.511-2.812a.454.454 0 0 0 .17-.354v-4.274l4.614-4.506a.454.454 0 0 0 -.317-.779z"/><path d="m11.085832 14.18196c3.399443 1.97457 6.855925-2.441094 4.074102-5.1815164-2.781825-2.7404217-7.2642008.6646174-5.2597994 4.0134654l-1.9001346 1.871854 1.1856973 1.168051zm1.699723-4.4945981c2.236109 0 2.236109 3.3042441 0 3.3042441-2.236108 0-2.236108-3.3042441 0-3.3042441z"/></g></svg> \ No newline at end of file